Compare commits
85 Commits
caota_test
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 15d9ce3b77 | |||
| 86889f33ce | |||
| 58371d3b84 | |||
| 76e46da8f2 | |||
| 26e131ca22 | |||
| 556b49e002 | |||
| 0615cccc06 | |||
| 07011002f3 | |||
| b0f25625ee | |||
| 2efeb53cec | |||
| 1c4fb6d701 | |||
| 7dfc0064d1 | |||
| 80ca84804e | |||
| bb249c873a | |||
| 25212af64f | |||
| 98efbe2c2e | |||
| 86d72a507a | |||
| 240684b574 | |||
| 35c06a9c5e | |||
| a304f35be8 | |||
| 0e0d56de07 | |||
| d84f30287a | |||
| d22b219432 | |||
| e99eb82222 | |||
| c908a21433 | |||
| cc21ae1559 | |||
| 94027e396e | |||
| 0f28596721 | |||
| 47a85b66f4 | |||
| 123230b8c9 | |||
| 13144ca430 | |||
| afd6906667 | |||
| eceb08fb7f | |||
| 8f7ef2d2ba | |||
| d5ee75bcf7 | |||
| cdb85e6eb2 | |||
| 4f22d9da41 | |||
| cd8a69210d | |||
| 1fdd9bda91 | |||
| 35922aed85 | |||
| b3e733888b | |||
| ed897c728f | |||
| 4865576862 | |||
| c8883bda11 | |||
| 3c23bf36ea | |||
| 2305f4761e | |||
| 7afcb9b8b1 | |||
| e346440748 | |||
| 08ef4dcb07 | |||
| cc12a9baa3 | |||
| 9e0679d4c8 | |||
| 9bcd435f6c | |||
| c0f911efc2 | |||
| 175a53080a | |||
| 9532f09cdd | |||
| bb83650b92 | |||
| 398b5ab56b | |||
| 4a0810acb3 | |||
| 0f37a80263 | |||
| 049f06604a | |||
| 52b30b5660 | |||
| 8efd6589bc | |||
|
|
15dc6603ba | ||
|
|
cbcdd3e19f | ||
|
|
c74f60de09 | ||
| dfbc1ea083 | |||
| c0a7f5bd9b | |||
| 5e933151ed | |||
| 59d42f81ac | |||
|
|
0e77614b89 | ||
|
|
63105f5c67 | ||
|
|
2917fd4159 | ||
|
|
d47e2a2378 | ||
| 5b8c73caea | |||
|
|
9cd021f474 | ||
|
|
1d1b8b29a7 | ||
| a6eeb30b82 | |||
| 7a706ba9f2 | |||
|
|
c142e0ccb6 | ||
|
|
fe2c58f65f | ||
|
|
e83c5f2d53 | ||
|
|
839b913b02 | ||
|
|
f742176c1e | ||
| 31ed53f0f1 | |||
| 02fdd57538 |
7
.vscode/launch.json
vendored
7
.vscode/launch.json
vendored
@@ -4,15 +4,16 @@
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
|
||||
{
|
||||
"type": "python",
|
||||
"request": "launch",
|
||||
"name": "Pserve",
|
||||
"python": "/Users/Thien-An/AppData/Local/Programs/Python/Python37",
|
||||
"program": "/pyramid/Scripts/pserve",
|
||||
"python": "C:/pyramid10/Scripts/python.exe",
|
||||
"program": "/pyramid10/Scripts/pserve.exe",
|
||||
"args": [
|
||||
"-v",
|
||||
"development.ini"
|
||||
"development.ini",
|
||||
],
|
||||
"console": "integratedTerminal",
|
||||
"debugOptions": [
|
||||
|
||||
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"html.validate.scripts": false,
|
||||
"editor.minimap.enabled": false,
|
||||
"python.pythonPath": "C:\\Users\\Thien-An\\AppData\\Local\\Programs\\Python\\Python37\\python.exe"
|
||||
"python.pythonPath": "C:\\pyramid10\\Scripts\\python.exe"
|
||||
}
|
||||
1
bitbucket app password.txt
Normal file
1
bitbucket app password.txt
Normal file
@@ -0,0 +1 @@
|
||||
9HMYPQ7Yz8emdBwGpbg3
|
||||
@@ -19,19 +19,27 @@ pyramid.includes =
|
||||
pyramid_tm
|
||||
|
||||
|
||||
#sqlalchemy.url = mysql://phuoc:phuoc!@localhost/bddevfac?charset=utf8
|
||||
sqlalchemy.url = mysql://phuoc:phuoc!@192.168.1.17/bddevfac?charset=utf8
|
||||
# sqlalchemy.url = mysql://dumas_ro:dumas_RO!@localhost/bddevfac?charset=utf8
|
||||
sqlalchemy.url = mysql://phuoc:phuoc!@localhost/bddevfac?charset=utf8
|
||||
# sqlalchemy.url = mysql://phuoc:phuoc!@192.168.0.31/bddevfac?charset=utf8
|
||||
|
||||
mondumas.admin_email = cao.thien-phuoc@orange.fr
|
||||
mondumas.admin_email = peinture-dumas@entreprise-dumas.com
|
||||
mondumas.devfac_url = mondumas:static/DEVFAC/DOCS_ATTACHES/
|
||||
mondumas.devfac_dir = /DEVFAC14/DOCS_ATTACHES
|
||||
|
||||
# Mailer configuration
|
||||
mail.host = smtp.orange.fr
|
||||
mail.port = 25
|
||||
mail.username = cao.thien-phuoc@orange.fr
|
||||
mail.host = smtp.office365.com
|
||||
mail.port = 587
|
||||
mail.tls = True
|
||||
mail.username = peinture-dumas@entreprise-dumas.com
|
||||
mail.password = Nar50611
|
||||
|
||||
# SMTP RELAY alinto
|
||||
# mail.host = gatewayxl.alinto.net
|
||||
# mail.port = 465
|
||||
# mail.ssl = True
|
||||
# mail.username = smtpsasdumas@entreprise-dumas.com
|
||||
|
||||
[server:main]
|
||||
use = egg:waitress#main
|
||||
host = 0.0.0.0
|
||||
|
||||
49
imap-over-tls-example.py
Normal file
49
imap-over-tls-example.py
Normal file
@@ -0,0 +1,49 @@
|
||||
#!/usr/bin/env python3
|
||||
import imaplib
|
||||
import msal
|
||||
import pprint
|
||||
|
||||
conf = {
|
||||
"authority": "https://login.microsoftonline.com/47f02128-acf0-47a7-a3bf-27a3619d4a4f",
|
||||
"client_id": "4fdf2634-a260-4576-a442-684998f0187b", #AppID
|
||||
"scope": ['https://outlook.office365.com/.default'],
|
||||
"secret": "dZD8Q~GmG_PFi.t_uRiyBEzwLeMZeemQGHUFta~J", #Key-Value
|
||||
"secret-id": "5cae3249-0fc0-43e2-bc0e-d78f41964970", #Key-ID
|
||||
}
|
||||
|
||||
def generate_auth_string(user, token):
|
||||
return f"user={user}\x01auth=Bearer {token}\x01\x01"
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = msal.ConfidentialClientApplication(conf['client_id'], authority=conf['authority'],
|
||||
client_credential=conf['secret'])
|
||||
|
||||
result = app.acquire_token_silent(conf['scope'], account=None)
|
||||
|
||||
if not result:
|
||||
print("No suitable token in cache. Get new one.")
|
||||
result = app.acquire_token_for_client(scopes=conf['scope'])
|
||||
|
||||
if "access_token" in result:
|
||||
print(result['token_type'])
|
||||
pprint.pprint(result)
|
||||
else:
|
||||
print(result.get("error"))
|
||||
print(result.get("error_description"))
|
||||
print(result.get("correlation_id"))
|
||||
|
||||
|
||||
# imap = imaplib.IMAP4('outlook.office365.com')
|
||||
# imap.starttls()
|
||||
imap = imaplib.IMAP4_SSL('outlook.office365.com', 993)
|
||||
imap.authenticate("XOAUTH2", lambda x: generate_auth_string("polynet-dumas@entreprise-dumas.com",
|
||||
result['access_token']).encode("utf-8"))
|
||||
|
||||
# Print list of mailboxes on server
|
||||
code, mailboxes = imap.list()
|
||||
for mailbox in mailboxes:
|
||||
print(mailbox.decode("utf-8"))
|
||||
# Select mailbox
|
||||
imap.select("INBOX")
|
||||
# Cleanup
|
||||
imap.close()
|
||||
@@ -2,12 +2,10 @@ Metadata-Version: 2.1
|
||||
Name: mondumas
|
||||
Version: 1.0
|
||||
Summary: mondumas
|
||||
Home-page: UNKNOWN
|
||||
Home-page:
|
||||
Author:
|
||||
Author-email:
|
||||
License: UNKNOWN
|
||||
Keywords: web wsgi bfg pylons pyramid
|
||||
Platform: UNKNOWN
|
||||
Classifier: Programming Language :: Python
|
||||
Classifier: Framework :: Pyramid
|
||||
Classifier: Topic :: Internet :: WWW/HTTP
|
||||
@@ -56,5 +54,3 @@ Elle est développée avec les composants open source suivants :
|
||||
---
|
||||
|
||||
- Initial version
|
||||
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
CHANGES.txt
|
||||
MANIFEST.in
|
||||
README.md
|
||||
bitbucket app password.txt
|
||||
development.ini
|
||||
diff.txt
|
||||
production.ini
|
||||
pytest.ini
|
||||
setup.py
|
||||
|
||||
@@ -1,4 +1,2 @@
|
||||
[paste.app_factory]
|
||||
main = mondumas:main
|
||||
[console_scripts]
|
||||
|
||||
[paste.app_factory]
|
||||
main = mondumas:main
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
pyramid
|
||||
pyramid==1.10
|
||||
pyramid_chameleon
|
||||
pyramid_debugtoolbar
|
||||
pyramid_layout
|
||||
pyramid_mailer
|
||||
pyramid_tm
|
||||
SQLAlchemy
|
||||
SQLAlchemy==1.2.19
|
||||
transaction
|
||||
zope.sqlalchemy==1.1
|
||||
waitress
|
||||
mysqlclient
|
||||
mysqlclient==1.4
|
||||
docutils
|
||||
pdfkit
|
||||
python-dateutil
|
||||
|
||||
@@ -23,7 +23,7 @@ def execute_query(request, query, params):
|
||||
def get_users_agenda(request, user):
|
||||
if user == '':
|
||||
""" lire la liste des users ayant un agenda"""
|
||||
query = "SELECT * FROM p_users WHERE cd_uti != 'N' and cd_uti = agenda AND actif != 0 ORDER BY nom;"
|
||||
query = "SELECT * FROM p_users WHERE cd_uti = agenda AND actif != 0 ORDER BY nom;"
|
||||
results = request.dbsession.execute(query).fetchall()
|
||||
else:
|
||||
""" lire le user ayant un agenda"""
|
||||
|
||||
@@ -33,31 +33,29 @@ def get_member_by_mdp_oublie(request, lien):
|
||||
|
||||
def get_member_by_id(request, mbr_id):
|
||||
if mbr_id == '0':
|
||||
query = "SELECT * FROM p_users ORDER BY cd_uti"
|
||||
query = """SELECT p_users.*, p_acces.libelle FROM p_users
|
||||
INNER JOIN p_acces ON p_acces.code = p_users.access
|
||||
ORDER BY cd_uti"""
|
||||
results = request.dbsession.execute(query).fetchall()
|
||||
else:
|
||||
# lire le membres par son identifianr
|
||||
query = """SELECT * FROM p_users WHERE CD_UTI=:mbr_id;"""
|
||||
query = """SELECT p_users.*, p_acces.libelle FROM p_users
|
||||
INNER JOIN p_acces ON p_acces.code = p_users.access
|
||||
WHERE CD_UTI=:mbr_id;"""
|
||||
results = request.dbsession.execute(query, {'mbr_id': mbr_id}).first()
|
||||
return results
|
||||
|
||||
def get_member_info(request, logged_in):
|
||||
# lire le membres par son identifianr
|
||||
query = """SELECT nom, email, access, societe FROM p_users WHERE CD_UTI=:logged_in;"""
|
||||
query = """SELECT u.nom, u.email, u.access, u.societe, p_acces.libelle FROM p_users u
|
||||
INNER JOIN p_acces ON p_acces.code = u.access
|
||||
WHERE CD_UTI=:logged_in;"""
|
||||
results = request.dbsession.execute(query, {'logged_in': logged_in}).first()
|
||||
if results.access == 9:
|
||||
fonction = 'Administrateur'
|
||||
elif results.access == 8:
|
||||
fonction = 'Comptabilité'
|
||||
elif results.access == 5:
|
||||
fonction = 'Gestion'
|
||||
else:
|
||||
fonction = 'Production'
|
||||
|
||||
return {
|
||||
'nom': results.nom,
|
||||
'email': results.email,
|
||||
'fonction': fonction,
|
||||
'fonction': results.libelle,
|
||||
'societe': results.societe,
|
||||
}
|
||||
|
||||
@@ -88,9 +86,6 @@ def update_membre(request, cd_uti, new_values):
|
||||
for param in new_values.keys():
|
||||
if param == 'nom':
|
||||
new_values['nom'] = new_values['nom'].upper()
|
||||
if param == 'access':
|
||||
' ne prend que le 1er caractère'
|
||||
new_values['access'] = new_values['access'][0]
|
||||
|
||||
if s:
|
||||
s += ",%s=:%s" % (param, param)
|
||||
@@ -108,7 +103,7 @@ def delete_membre(request, cd_uti):
|
||||
query = "DELETE FROM p_users WHERE cd_uti = :cd_uti ;"
|
||||
execute_query(request, query, {'cd_uti': cd_uti})
|
||||
|
||||
def get_article(request, type, groupe, libelle):
|
||||
def get_tarif_ajax(request, type, groupe, libelle):
|
||||
if type == 'LIB':
|
||||
if groupe == 'TEXTE':
|
||||
# lire tous les textes dont le libelle commençe par
|
||||
@@ -130,35 +125,35 @@ def get_codespostaux(request, codep):
|
||||
return results
|
||||
|
||||
def get_dd_restant(request):
|
||||
query = """SELECT SUM(IF(societe='PE',1,0)) AS nb_PE,
|
||||
SUM(IF(societe='ME',1,0)) AS nb_ME,
|
||||
SUM(IF(societe='PL',1,0)) AS nb_PL
|
||||
FROM dem_devis WHERE STATUS=0;"""
|
||||
query = """SELECT (SELECT count(*) FROM dem_devis WHERE societe='PE' AND STATUS=0) AS nb_PE,
|
||||
(SELECT count(*) FROM dem_devis WHERE societe='ME' AND STATUS=0) AS nb_ME,
|
||||
(SELECT count(*) FROM dem_devis WHERE societe='PL' AND STATUS=0) AS nb_PL;"""
|
||||
results = request.dbsession.execute(query).first()
|
||||
return results
|
||||
|
||||
def get_de_restant(request):
|
||||
query = """SELECT SUM(IF(societe='PE',1,0)) AS nb_PE,
|
||||
SUM(IF(societe='ME',1,0)) AS nb_ME,
|
||||
SUM(IF(societe='PL',1,0)) AS nb_PL
|
||||
FROM devis WHERE STATUS<4;"""
|
||||
# les suivis des attachés commerciaux dnas les 4 derniers jours
|
||||
query = """SELECT
|
||||
(SELECT count(*) FROM dem_lig WHERE societe='PE' AND DATEDIFF(CURDATE(), date) <= 7 AND usermaj in ('CG','MP','RV','VD')) AS nb_PE,
|
||||
(SELECT count(*) FROM dem_lig WHERE societe='ME' AND DATEDIFF(CURDATE(), date) <= 7 AND usermaj in ('CG','MP','RV','VD')) AS nb_ME,
|
||||
(SELECT count(*) FROM dem_lig WHERE societe='PL' AND DATEDIFF(CURDATE(), date) <= 7 AND usermaj in ('CG','MP','RV','VD')) AS nb_PL;"""
|
||||
results = request.dbsession.execute(query).first()
|
||||
return results
|
||||
|
||||
def get_fa_restant(request):
|
||||
query = """SELECT SUM(IF(societe='PE',1,0)) AS nb_PE,
|
||||
SUM(IF(societe='ME',1,0)) AS nb_ME,
|
||||
SUM(IF(societe='PL',1,0)) AS nb_PL
|
||||
FROM facture WHERE STATUS<8;"""
|
||||
query = """SELECT (SELECT count(*) FROM facture WHERE societe='PE' AND STATUS < 8) AS nb_PE,
|
||||
(SELECT count(*) FROM facture WHERE societe='ME' AND STATUS < 8) AS nb_ME,
|
||||
(SELECT count(*) FROM facture WHERE societe='PL' AND STATUS < 8) AS nb_PL;"""
|
||||
results = request.dbsession.execute(query).first()
|
||||
return results
|
||||
|
||||
def get_rdv_by_date(request, date, agenda):
|
||||
query = """SELECT COUNT(*) AS nb_rdv FROM bddevfac.dem_lig WHERE DATEVI=:date AND LISTE=:agenda;"""
|
||||
results = request.dbsession.execute(query, {'date': date, 'agenda': agenda}).first()
|
||||
return results
|
||||
return results.nb_rdv
|
||||
|
||||
def get_rdf_null(request):
|
||||
query = """SELECT COUNT(*) AS nb_rdf FROM bddevfac.dem_rdf WHERE date_relu IS NULL;"""
|
||||
# lire les RDF non validés depuis moins d'un an
|
||||
query = """SELECT COUNT(*) AS nb_rdf FROM bddevfac.dem_rdf WHERE date_relu IS NULL AND year(date_inter) > 2021;"""
|
||||
results = request.dbsession.execute(query).first()
|
||||
return results
|
||||
return results.nb_rdf
|
||||
|
||||
@@ -25,19 +25,23 @@ def execute_query(request, query, params):
|
||||
mark_changed(request.dbsession)
|
||||
transaction.commit()
|
||||
|
||||
def get_devis_byName(request, societe, name):
|
||||
numero = to_int(name)
|
||||
|
||||
def get_dossiers_byName(request, societe, name):
|
||||
# lires tous les dossiers d'un chantier
|
||||
|
||||
if numero > 0:
|
||||
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
|
||||
FROM devis WHERE societe=:societe AND no_id >=:name AND web = 'W' LIMIT 300;;""" % (societe, name)
|
||||
elif len(name) == 0:
|
||||
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
|
||||
FROM devis WHERE societe=:societe AND web = 'W' ORDER BY no_id DESC LIMIT 300;"""
|
||||
else:
|
||||
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
|
||||
FROM devis WHERE societe=:societe AND c_nom LIKE ':name%' AND web = 'W' LIMIT 500)"""
|
||||
results = request.dbsession.execute(query, {'societe': societe, 'name': name}).fetchall()
|
||||
query = """select * from (
|
||||
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 dem_devis WHERE societe=:societe AND c_nom LIKE :name
|
||||
UNION
|
||||
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
|
||||
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
|
||||
|
||||
def get_devfac_by_no(request,nodossier):
|
||||
@@ -233,4 +237,16 @@ def update_devis_cloture(request, nodevis, status, logged_in):
|
||||
nochantier = int(nodevis[5:])
|
||||
# met le montant regle à 1 centime pour terminé le dossier
|
||||
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})
|
||||
|
||||
@@ -47,8 +47,14 @@ def get_dossier_by_sinistre(request,societe, nosin):
|
||||
results = request.dbsession.execute(query).first()
|
||||
return results
|
||||
|
||||
def get_dossiers_traites(request):
|
||||
query = "SELECT d.*, s.libelle FROM dem_devis d JOIN p_statuts s ON d.STATUS = s.CODE WHERE d.STATUS < 2 ORDER BY d.societe, d.STATUS, d.nomcli";
|
||||
def get_dossiers_traites(request, societe):
|
||||
query = """SELECT d.date, LPAD(d.no_id,6,'0') AS numero, d.nomcli, CONCAT(d.c_nom,'; ',d.c_adr,'; ',d.c_ville) AS chantier, d.mttrav AS montant, status, s.libelle, d.nosin, d.nopol, d.humidite, d.usermaj
|
||||
FROM dem_devis d JOIN p_statuts s ON d.STATUS = s.CODE WHERE d.societe = :societe AND d.status < 2 ORDER BY d.status, d.nomcli""";
|
||||
results = request.dbsession.execute(query, {'societe': societe}).fetchall()
|
||||
return results
|
||||
|
||||
def get_dossiers_importes(request):
|
||||
query = "SELECT * FROM dem_devis WHERE usermaj='EMAIL' and status=0 ORDER BY DATEMAJ";
|
||||
results = request.dbsession.execute(query).fetchall()
|
||||
return results
|
||||
|
||||
@@ -57,7 +63,13 @@ def get_clients_byName(request, societe, nom):
|
||||
results = request.dbsession.execute(query).fetchall()
|
||||
return results
|
||||
|
||||
|
||||
def get_derniers_suivis(request):
|
||||
query = """SELECT l.*, CONCAT(l.societe,'-',l.no_id) AS nodossier, d.C_NOM FROM dem_lig l
|
||||
INNER JOIN dem_devis d ON l.societe=d.societe AND l.no_id=d.no_id
|
||||
WHERE DATEDIFF(CURDATE(), l.date) <= 7 AND l.usermaj in ('CG','MP','RV','VD') ORDER BY l.date, l.societe DESC;"""
|
||||
results = request.dbsession.execute(query, ).fetchall()
|
||||
return results
|
||||
|
||||
|
||||
def get_dossier_rdv_by_no(request,nodossier, nolig):
|
||||
societe = nodossier[0:2]
|
||||
@@ -286,6 +298,16 @@ def update_rapport_client(request, norapport, nomClient, codeClient):
|
||||
query = "UPDATE dem_rdf SET CD_CLI = :cd_cli, NOMCLI = :nomClient WHERE no_id = :norapport"
|
||||
execute_query(request, query, {'norapport': norapport, 'cd_cli': cd_cli, 'nomClient': nomClient})
|
||||
|
||||
def update_rapport_nochantier(request, norapport, new_nochantier):
|
||||
# controler que le no du nouveau dossier existe
|
||||
dossier = get_dossier_by_no(request,'PL-' + new_nochantier)
|
||||
if dossier:
|
||||
query = "UPDATE dem_rdf SET nochantier = :new_nochantier WHERE no_id = :norapport"
|
||||
execute_query(request, query, {'norapport': norapport, 'new_nochantier': new_nochantier})
|
||||
return "OK"
|
||||
else:
|
||||
return "NOK"
|
||||
|
||||
def insert_facture_rdf(request, societe, nochantier, cd_cli, nomcli, user, ref, date_rapport):
|
||||
# créer une facture vierge à partir du dossier
|
||||
query = "CALL spINS_FACTURE_RDF(:societe, :nochantier, :cd_cli, :nomcli, :user, :ref, :date_rapport)"
|
||||
@@ -338,12 +360,51 @@ def get_status_by_id(request, code):
|
||||
results = request.dbsession.execute(query, {'code': code}).first()
|
||||
return results
|
||||
|
||||
def get_devis_en_att(request):
|
||||
query = "SELECT d.*, s.libelle FROM devis d JOIN p_statuts s ON d.STATUS = s.CODE WHERE d.STATUS < 4 ORDER BY d.societe, d.STATUS, d.nomcli;"
|
||||
results = request.dbsession.execute(query).fetchall()
|
||||
def get_motifs(request):
|
||||
query = """SELECT * FROM p_motifs;"""
|
||||
results = request.dbsession.execute(query,).fetchall()
|
||||
return results
|
||||
|
||||
def get_factures_en_att(request):
|
||||
query = "SELECT f.*, s.libelle FROM facture f JOIN p_statuts s ON f.STATUS = s.CODE WHERE f.STATUS < 8 ORDER BY f.societe, f.STATUS, f.nomcli;"
|
||||
results = request.dbsession.execute(query).fetchall()
|
||||
return results
|
||||
def get_factures_en_att(request, societe):
|
||||
query = """SELECT f.date, LPAD(f.no_id,6,'0') AS numero, f.nomcli, CONCAT(f.c_nom,'; ',f.c_adr,'; ',f.c_ville) AS chantier, f.totalht AS montant, f.status, s.libelle, f.nosin, f.nopol, f.usermaj
|
||||
FROM facture f JOIN p_statuts s ON f.STATUS = s.CODE WHERE f.societe=:societe AND f.STATUS < 8 ORDER BY f.societe, f.STATUS, f.nomcli;"""
|
||||
results = request.dbsession.execute(query, {'societe': societe}).fetchall()
|
||||
return results
|
||||
|
||||
def get_dem_notes(request, nodossier, noligne):
|
||||
societe = nodossier[0:2]
|
||||
nochantier = int(nodossier[3:])
|
||||
|
||||
if noligne == '0':
|
||||
query = "SELECT societe, nochantier, noligne, libelle FROM dem_notes WHERE societe = :societe AND nochantier = :nochantier ORDER BY libelle;"
|
||||
results = request.dbsession.execute(query, {'societe': societe, 'nochantier': nochantier, 'noligne': noligne}).fetchall()
|
||||
else:
|
||||
query = "SELECT * FROM dem_notes WHERE societe = :societe AND nochantier = :nochantier AND noligne = :noligne;"
|
||||
results = request.dbsession.execute(query, {'societe': societe, 'nochantier': nochantier, 'noligne': noligne}).first()
|
||||
return results
|
||||
|
||||
def delete_dem_note(request, nodossier, noligne):
|
||||
societe = nodossier[0:2]
|
||||
nochantier = int(nodossier[3:])
|
||||
# une note ou dessin
|
||||
query = "DELETE FROM dem_notes WHERE societe=:societe AND nochantier=:nochantier AND noligne=:noligne;"
|
||||
execute_query(request, query, {'societe': societe, 'nochantier': nochantier, 'noligne': noligne})
|
||||
|
||||
def update_dem_note(request, nodossier, noligne, notes, logged_in):
|
||||
societe = nodossier[0:2]
|
||||
nochantier = int(nodossier[3:])
|
||||
# création ou modif ?
|
||||
if noligne == '0':
|
||||
query = "INSERT INTO dem_notes (societe, nochantier, notes, usermaj) VALUES (:societe, :nochantier, :notes, :logged_in);"
|
||||
execute_query(request, query, {'societe': societe, 'nochantier': nochantier, 'notes': notes, 'logged_in': logged_in})
|
||||
else:
|
||||
query = "UPDATE dem_notes SET notes=:notes, logged_in=:logged_in WHERE societe=:societe AND nochantier=:nochantier AND noligne=:noligne;"
|
||||
execute_query(request, query, {'societe': societe, 'nochantier': nochantier, 'noligne': noligne, 'notes': notes, 'logged_in': logged_in})
|
||||
|
||||
def get_nb_dessins(request, nodossier):
|
||||
societe = nodossier[0:2]
|
||||
nochantier = nodossier[3:]
|
||||
|
||||
query = "SELECT count(*) AS nb FROM dossier_attaches WHERE societe=:societe AND nochantier=:nochantier AND origine='FRN' AND nomfichier LIKE '%DESSIN No %';"
|
||||
results = request.dbsession.execute(query, {'societe': societe, 'nochantier': nochantier}).first()
|
||||
return results.nb
|
||||
|
||||
@@ -1,17 +1,8 @@
|
||||
# -*- coding: utf8 -*-
|
||||
from sqlalchemy import text
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
from sqlalchemy.orm import (
|
||||
scoped_session,
|
||||
sessionmaker,
|
||||
)
|
||||
from zope.sqlalchemy import (
|
||||
ZopeTransactionExtension,
|
||||
mark_changed
|
||||
)
|
||||
|
||||
from datetime import *
|
||||
import dateutil.relativedelta
|
||||
import transaction
|
||||
|
||||
def execute_query(request, query, params):
|
||||
@@ -26,6 +17,25 @@ def get_log_nuit(request, ):
|
||||
results = request.dbsession.execute(query, )
|
||||
return results.fetchall()
|
||||
|
||||
def get_last_modified(request,):
|
||||
query = """
|
||||
SELECT 'DEMANDES' as nomtable, societe, no_id, datemaj, usermaj FROM bddevfac.dem_devis where date(datemaj)=CURDATE()
|
||||
UNION
|
||||
SELECT 'DEM_LIGNE' as nomtable, societe, no_id, datemaj, usermaj FROM bddevfac.dem_lig where date(datemaj)=CURDATE()
|
||||
UNION
|
||||
SELECT 'DEVIS' as nomtable, societe, no_id, datemaj, usermaj FROM bddevfac.devis where date(datemaj)=CURDATE()
|
||||
UNION
|
||||
SELECT 'DEVIS_LIG' as nomtable, societe, no_id, datemaj, usermaj FROM bddevfac.devis_lig where date(datemaj)=CURDATE()
|
||||
UNION
|
||||
SELECT 'FACTURES' as nomtable, societe, no_id, datemaj, usermaj FROM bddevfac.facture where date(datemaj)=CURDATE()
|
||||
UNION
|
||||
SELECT 'FACT_LIG' as nomtable, societe, no_id, datemaj, usermaj FROM bddevfac.facture_lig where date(datemaj)=CURDATE()
|
||||
UNION
|
||||
SELECT 'FAC_REGL' as nomtable, societe, cod_bnq, date as datemaj, JST FROM bddevfac.facture_reg where date(date)=CURDATE()
|
||||
order by datemaj desc limit 10; """
|
||||
results = request.dbsession.execute(query, )
|
||||
return results.fetchall()
|
||||
|
||||
def get_rappels_rdv(request):
|
||||
"""Lire les 200 derniers envois d'emails"""
|
||||
query = "SELECT * FROM email_rappels ORDER BY no_id DESC LIMIT 400;"
|
||||
@@ -33,7 +43,6 @@ def get_rappels_rdv(request):
|
||||
return results.fetchall()
|
||||
|
||||
def get_dossiers_byChantier(request, societe, name):
|
||||
|
||||
query = "CALL spGET_DOSSIERS_byChantier('%s','%s','%s');" % (societe, 'DE', name.replace("'","''"))
|
||||
results = request.dbsession.execute(query).fetchall()
|
||||
return results
|
||||
@@ -114,6 +123,11 @@ def get_articles_byFam(request, fam):
|
||||
results = request.dbsession.execute(query, ).fetchall()
|
||||
return results
|
||||
|
||||
def get_article(request, ref):
|
||||
query = "SELECT * FROM articles WHERE REF = ref;"
|
||||
results = request.dbsession.execute(query, ).first()
|
||||
return results
|
||||
|
||||
def update_article(request, ref, new_values):
|
||||
# formater les champs
|
||||
s = ''
|
||||
@@ -226,4 +240,9 @@ def get_tarifs_byGroupe(request, groupe):
|
||||
def get_tarif(request, groupe, ref):
|
||||
query = "SELECT * FROM tarifs WHERE groupe = :groupe and ref = :ref;"
|
||||
results = request.dbsession.execute(query, {'groupe': groupe, 'ref': ref}).first()
|
||||
return results
|
||||
return results
|
||||
|
||||
def get_p_acces(request):
|
||||
query = """SELECT * FROM p_acces ORDER BY code;"""
|
||||
results = request.dbsession.execute(query).fetchall()
|
||||
return results
|
||||
|
||||
@@ -82,6 +82,20 @@ def get_ca_groupe_3y(request, societe, year):
|
||||
results = request.dbsession.execute(query, {'societe': societe, 'year': year})
|
||||
return results.fetchall()
|
||||
|
||||
def get_ca_groupe_3y_with_others(request, societe, year):
|
||||
|
||||
query = """SELECT groupe,
|
||||
SUM(IF (year(date) = :year - 2, TOTALHT, 0)) as Annee1,
|
||||
SUM(IF (year(date) = :year - 2, 1, 0)) as Count1,
|
||||
SUM(IF (year(date) = :year - 1, TOTALHT, 0)) as Annee2,
|
||||
SUM(IF (year(date) = :year - 1, 1, 0)) as Count2,
|
||||
SUM(IF (year(date) = :year, TOTALHT, 0)) as Annee3,
|
||||
SUM(IF (year(date) = :year, 1, 0)) as Count3
|
||||
FROM bddevfac.facture
|
||||
WHERE societe=:societe AND year(date) >= :year - 2 AND typecli <> 'I' GROUP BY groupe;"""
|
||||
results = request.dbsession.execute(query, {'societe': societe, 'year': year})
|
||||
return results.fetchall()
|
||||
|
||||
def get_ca_clients_12m(request, societe, datedeb, datefin):
|
||||
|
||||
query = """SELECT DATE_FORMAT(date, "%Y%m") as yymm,
|
||||
@@ -127,3 +141,32 @@ def get_delais_pourcent(request, societe, groupe, datedeb):
|
||||
WHERE societe = :societe AND GROUPE = :groupe AND date >= :datedeb;"""
|
||||
results = request.dbsession.execute(query, {'societe': societe, 'groupe': groupe, 'datedeb': datedeb.strftime("%Y-%m")})
|
||||
return results.fetchall()
|
||||
|
||||
def get_nb_devis_fact(request, societe, year):
|
||||
|
||||
query = """SELECT
|
||||
SUM(IF (NOFACT > 0 AND year(date) = :year - 2, 1, 0)) AS devis_fact_y1,
|
||||
SUM(IF (NOFACT <= 0 AND year(date) = :year - 2, 1, 0)) AS devis_non_fact_y1,
|
||||
SUM(IF (NOFACT > 0 AND year(date) = :year - 1, 1, 0)) AS devis_fact_y2,
|
||||
SUM(IF (NOFACT <= 0 AND year(date) = :year - 1, 1, 0)) AS devis_non_fact_y2,
|
||||
SUM(IF (NOFACT > 0 AND year(date) = :year, 1, 0)) AS devis_fact_y3,
|
||||
SUM(IF (NOFACT <= 0 AND year(date) = :year, 1, 0)) AS devis_non_fact_y3
|
||||
FROM bddevfac.devis
|
||||
WHERE societe = :societe;"""
|
||||
results = request.dbsession.execute(query, {'societe': societe, 'year': year})
|
||||
return results.fetchall()
|
||||
|
||||
def get_nb_fact_with_devis(request, societe, year):
|
||||
|
||||
query = """SELECT
|
||||
SUM(IF (NODEVIS > 0 AND year(date) = :year - 2, 1, 0)) AS fact_w_devis_y1,
|
||||
SUM(IF (NODEVIS <= 0 AND year(date) = :year - 2, 1, 0)) AS fact_wo_devis_y1,
|
||||
SUM(IF (NODEVIS > 0 AND year(date) = :year - 1, 1, 0)) AS fact_w_devis_y2,
|
||||
SUM(IF (NODEVIS <= 0 AND year(date) = :year - 1, 1, 0)) AS fact_wo_devis_y2,
|
||||
SUM(IF (NODEVIS > 0 AND year(date) = :year, 1, 0)) AS fact_w_devis_y3,
|
||||
SUM(IF (NODEVIS <= 0 AND year(date) = :year, 1, 0)) AS fact_wo_devis_y3
|
||||
FROM bddevfac.facture
|
||||
WHERE societe = :societe;"""
|
||||
results = request.dbsession.execute(query, {'societe': societe, 'year': year})
|
||||
return results.fetchall()
|
||||
|
||||
|
||||
@@ -139,22 +139,25 @@ def purge_mensuelle(request, until_date):
|
||||
WHERE facture.date < :until_date AND ABS(facture.totalttc - facture.mtregl) < 1;"""
|
||||
execute_query(request, query, {'until_date': until_date})
|
||||
|
||||
# ----- Purger les FACTURES réglées antérieure à until_date
|
||||
# ----- Purger les FACTURES réglées antérieures à until_date
|
||||
query = """DELETE FROM facture WHERE facture.date < :until_date AND ABS(facture.totalttc - facture.mtregl) < 1;"""
|
||||
execute_query(request, query, {'until_date': until_date})
|
||||
# ---- Purger les VERSEMENTS inutilisés et antérieurs à until_date
|
||||
query = "DELETE FROM liv_bnq WHERE date < :until_date AND ABS(MontantRegl - MontantDebit) < 1;"
|
||||
execute_query(request, query, {'until_date': until_date})
|
||||
|
||||
# ----- Purger les DEVIS n'ayant pas de facture antérieure à until_date
|
||||
# ----- Purger les DEVIS n'ayant pas de facture antérieure à until_date
|
||||
query = """DELETE FROM devis WHERE devis.date < :until_date AND nofact=0;"""
|
||||
execute_query(request, query, {'until_date': until_date})
|
||||
|
||||
# -- RAZ les liens FACTURE et DEVIS
|
||||
query = "UPDATE dem_devis SET nodevis = 0, nofact = 0, datemaj=datemaj WHERE dem_devis.date < :until_date;"
|
||||
execute_query(request, query, {'until_date': until_date})
|
||||
# -- recreer les lien DEVIS
|
||||
# -- recreer les lien factures
|
||||
query = """UPDATE dem_devis INNER JOIN facture ON dem_devis.societe = facture.societe and dem_devis.no_id = facture.nochantier
|
||||
SET dem_devis.nofact = facture.no_id, dem_devis.datemaj=dem_devis.datemaj WHERE dem_devis.date < :until_date;"""
|
||||
execute_query(request, query, {'until_date': until_date})
|
||||
# -- recreer les lien factures
|
||||
# -- recreer les lien DEVIS
|
||||
query = """UPDATE dem_devis INNER JOIN devis ON dem_devis.societe = devis.societe and dem_devis.no_id = devis.nochantier
|
||||
SET dem_devis.nodevis = devis.no_id, dem_devis.datemaj=dem_devis.datemaj WHERE dem_devis.date < :until_date"""
|
||||
execute_query(request, query, {'until_date': until_date})
|
||||
@@ -183,6 +186,11 @@ def get_last_devis_client(request, societe, cd_cli):
|
||||
results = request.dbsession.execute(query, {'societe': societe, 'cd_cli': cd_cli}).first()
|
||||
return results
|
||||
|
||||
def get_last_proforma_client(request, societe, cd_cli):
|
||||
query = "SELECT * FROM proforma WHERE societe = :societe AND cd_cli = :cd_cli order by date DESC LIMIT 1;"
|
||||
results = request.dbsession.execute(query, {'societe': societe, 'cd_cli': cd_cli}).first()
|
||||
return results
|
||||
|
||||
def get_last_chantier_client(request, societe, cd_cli):
|
||||
query = "SELECT * FROM dem_devis WHERE societe = :societe AND cd_cli = :cd_cli order by date DESC LIMIT 1;"
|
||||
results = request.dbsession.execute(query, {'societe': societe, 'cd_cli': cd_cli}).first()
|
||||
@@ -198,10 +206,27 @@ def update_client_dern_operation(request, societe, cd_cli, dern_operation):
|
||||
execute_query(request, query, {'societe': societe, 'cd_cli': cd_cli})
|
||||
|
||||
def delete_client_unused(request):
|
||||
query = "DELETE FROM clients WHERE cd_cli <> 1 AND dern_operation IS NULL AND YEAR(cree_le) < YEAR(CURRENT_DATE()) - 2;"
|
||||
query = "DELETE FROM clients WHERE cd_cli <> 1 AND dern_operation IS NULL AND YEAR(cree_le) < YEAR(CURRENT_DATE()) - 5;"
|
||||
execute_query(request, query, {})
|
||||
|
||||
def update_stats_delais(request, societe, date, groupe):
|
||||
query = "CALL spUPD_STATS_DELAIS(:societe, :date, :groupe);"
|
||||
execute_query(request, query, {'societe': societe, 'date': date, 'groupe': groupe})
|
||||
|
||||
def update_devis_statut_4(request):
|
||||
# lire tutes les lignes de dem_devis mentionnant la commande
|
||||
query = "SELECT * FROM dem_lig where comment like '% est COMMANDE %';"
|
||||
devis_cdes = request.dbsession.execute(query, {}).fetchall()
|
||||
|
||||
for item in devis_cdes:
|
||||
if item.COMMENT.find('!! DE') == 0:
|
||||
# recupère le no de devis commandé
|
||||
nodevis = item.COMMENT[5:11]
|
||||
|
||||
# maj status de dem_devis concernée
|
||||
query = "UPDATE dem_devis SET status = 4, DATEMAJ = DATEMAJ WHERE societe = :societe AND no_id = :no_id AND status < 4;"
|
||||
execute_query(request, query, {'societe': item.societe, 'no_id': item.NO_ID})
|
||||
|
||||
# maj status de devis concerné
|
||||
query = "UPDATE devis SET status = 4, DATEMAJ = DATEMAJ WHERE societe = :societe AND no_id = :no_id AND status < 4;"
|
||||
execute_query(request, query, {'societe': item.societe, 'no_id': nodevis})
|
||||
|
||||
@@ -5,7 +5,7 @@ def includeme(config):
|
||||
config.add_route('planning', '/planning/{date}')
|
||||
config.add_route('rdv_edit','/rdv_edit/{nodossier}/{nolig}')
|
||||
# default
|
||||
config.add_route('new_home', '/')
|
||||
config.add_route('home', '/')
|
||||
config.add_route('affiche_message','/affiche_message/{login}')
|
||||
config.add_route('ajax_article', '/ajax_article')
|
||||
config.add_route('ajax_client', '/ajax_client')
|
||||
@@ -22,24 +22,35 @@ def includeme(config):
|
||||
# devis
|
||||
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_list', '/devis_list')
|
||||
config.add_route('devis_list', '/devis_list/{societe}/{nodevis}')
|
||||
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_view', '/devis_view/{nodevis}')
|
||||
config.add_route('devis_preview', '/devis_preview/{nodevis}')
|
||||
config.add_route('devis_select', '/devis_select/{date}')
|
||||
config.add_route('devis_selected', '/devis_selected/{goto}/{date}/{nodevis}')
|
||||
config.add_route('factures_en_att','/factures_en_att')
|
||||
config.add_route('facture_select', '/facture_select/{date}')
|
||||
config.add_route('facture_selected', '/facture_selected/{goto}/{date}/{nofacture}')
|
||||
# dossier
|
||||
config.add_route('demandes','/demandes')
|
||||
config.add_route('demandes_dl','/demandes_dl/{societe}/{email_from}/{email_uid}')
|
||||
config.add_route('dem_devis','/dem_devis')
|
||||
config.add_route('delete_img','/delete_img/{nodossier}/{norapport}/{origine}/{nomfic}')
|
||||
config.add_route('dern_suivis','/dern_suivis')
|
||||
config.add_route('dessin_edit','/dessin_edit/{nodossier}/{noligne}')
|
||||
config.add_route('dossier_edit', '/dossier_edit/{nodossier}')
|
||||
config.add_route('dossier_lookup', '/dossier_lookup')
|
||||
config.add_route('dossier_select', '/dossier_select/{date}')
|
||||
config.add_route('dossier_selected', '/dossier_selected/{goto}/{date}/{nodossier}')
|
||||
config.add_route('dossier_view', '/dossier_view/{nodossier}')
|
||||
config.add_route('note_edit','/note_edit/{nodossier}/{noligne}')
|
||||
config.add_route('rdf_bill','/rdf_bill/{no_id}')
|
||||
config.add_route('rdf_client','/rdf_client/{no_id}')
|
||||
config.add_route('rdf_edit','/rdf_edit/{nodossier}/{date_inter}')
|
||||
config.add_route('rdf_list','/rdf_list')
|
||||
config.add_route('rdf_nochantier','/rdf_nodossier/{no_id}')
|
||||
config.add_route('rdf_rapport','/rdf_rapport/{no_id}')
|
||||
config.add_route('rdf_view','/rdf_view/{no_id}')
|
||||
config.add_route('rotate_img','/rotate_img/{nodossier}/{norapport}/{origine}/{nomfic}/{angle}')
|
||||
@@ -47,9 +58,6 @@ def includeme(config):
|
||||
config.add_route('upload_doc', '/upload_doc/{nodossier}/{origine}')
|
||||
config.add_route('upload_img', '/upload_img/{norapport}/{origine}')
|
||||
config.add_route('upload_om', '/upload_om')
|
||||
config.add_route('dem_devis','/dem_devis')
|
||||
config.add_route('devis_en_att','/devis_en_att')
|
||||
config.add_route('factures_en_att','/factures_en_att')
|
||||
# parametres
|
||||
config.add_route('parametres', '/parametres')
|
||||
config.add_route('article_edit', '/article_edit/{ref}')
|
||||
@@ -60,7 +68,6 @@ def includeme(config):
|
||||
config.add_route('dashboard', '/dashboard')
|
||||
config.add_route('emails_msg', '/emails_msg/{nolig}')
|
||||
config.add_route('expert_edit', '/expert_edit/{code_cab}/{code_exp}')
|
||||
config.add_route('infrastructure', '/infrastructure')
|
||||
config.add_route('rappels_rdv', '/rappels_rdv')
|
||||
config.add_route('rdf_cause_edit', '/rdf_cause_edit/{code}')
|
||||
config.add_route('rdf_causes', '/rdf_causes')
|
||||
@@ -75,7 +82,6 @@ def includeme(config):
|
||||
config.add_route('user_edit', '/user_edit/{cd_uti}')
|
||||
config.add_route('users', '/users')
|
||||
config.add_route('users_ua', '/users_ua')
|
||||
config.add_route('home', '/new_home')
|
||||
|
||||
# stats
|
||||
config.add_route('stats', '/stats')
|
||||
@@ -84,6 +90,7 @@ def includeme(config):
|
||||
config.add_route('ca_groupes', '/ca_groupes/{societe}')
|
||||
config.add_route('ca_clients', '/ca_clients/{societe}')
|
||||
config.add_route('delais_pourcentage', '/delais_pourcentage/{societe}')
|
||||
config.add_route('pourcentage_devis', '/pourcentage_devis/{societe}')
|
||||
|
||||
# utils
|
||||
config.add_route('batch_nuit', '/batch_nuit/{param}')
|
||||
|
||||
@@ -276,6 +276,10 @@ color: black;
|
||||
}
|
||||
}
|
||||
|
||||
#dessin {
|
||||
width: 1140px;
|
||||
height: 1140px;
|
||||
}
|
||||
|
||||
/* ne pas affichier l'url after the link */
|
||||
@media print {
|
||||
@@ -283,3 +287,31 @@ color: black;
|
||||
content: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.info-box {
|
||||
display: block;
|
||||
min-height: 70px;
|
||||
background: #fff;
|
||||
width: 100%;
|
||||
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
|
||||
border-radius: 2px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
.info-box-icon {
|
||||
border-top-left-radius: 2px;
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
border-bottom-left-radius: 2px;
|
||||
display: block;
|
||||
float: left;
|
||||
height: 70px;
|
||||
width: 70px;
|
||||
text-align: center;
|
||||
font-size: 40px;
|
||||
line-height: 70px;
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
.info-box-content {
|
||||
padding: 5px 5px;
|
||||
margin-left: 80px;
|
||||
}
|
||||
88
mondumas/static/dist/drawingboard/drawingboard.css
vendored
Normal file
88
mondumas/static/dist/drawingboard/drawingboard.css
vendored
Normal file
@@ -0,0 +1,88 @@
|
||||
/* drawingboard.js v0.4.6 - https://github.com/Leimi/drawingboard.js
|
||||
* Copyright (c) 2015 Emmanuel Pelletier
|
||||
* Licensed MIT */
|
||||
.drawing-board, .drawing-board * { -webkit-box-sizing: content-box; -moz-box-sizing: content-box; box-sizing: content-box; }
|
||||
|
||||
.drawing-board-utils-hidden, .drawing-board-controls-hidden { display: none !important; }
|
||||
|
||||
.drawing-board { position: relative; display: block; }
|
||||
|
||||
.drawing-board-canvas-wrapper { position: relative; margin: 0; border: 1px solid #ddd; }
|
||||
|
||||
.drawing-board-canvas { position: absolute; top: 0; left: 0; z-index: 10; width: auto; }
|
||||
|
||||
.drawing-board-canvas { cursor: crosshair; z-index: 20; }
|
||||
|
||||
.drawing-board-cursor { position: absolute; top: 0; left: 0; pointer-events: none; border-radius: 50%; background: #ccc; background: rgba(0, 0, 0, 0.2); z-index: 30; }
|
||||
|
||||
.drawing-board-control > button, .drawing-board-control-colors-rainbows, .drawing-board-control-size .drawing-board-control-inner, .drawing-board-control-size-dropdown { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; overflow: hidden; border: none; background-color: #eee; padding: 2px 4px; border: 1px solid #ccc; box-shadow: 0 1px 3px -2px #121212, inset 0 2px 5px 0 rgba(255, 255, 255, 0.3); -webkit-box-shadow: 0 1px 3px -2px #121212, inset 0 2px 5px 0 rgba(255, 255, 255, 0.3); height: 28px; }
|
||||
|
||||
.drawing-board-control > button { cursor: pointer; min-width: 28px; line-height: 14px; }
|
||||
.drawing-board-control > button:hover, .drawing-board-control > button:focus { background-color: #ddd; }
|
||||
.drawing-board-control > button:active, .drawing-board-control > button.active { box-shadow: inset 0 1px 2px 0 rgba(0, 0, 0, 0.2); -webkit-box-shadow: inset 0 1px 2px 0 rgba(0, 0, 0, 0.2); background-color: #ddd; }
|
||||
.drawing-board-control > button[disabled] { color: gray; }
|
||||
.drawing-board-control > button[disabled]:hover, .drawing-board-control > button[disabled]:focus, .drawing-board-control > button[disabled]:active, .drawing-board-control > button[disabled].active { background-color: #eee; box-shadow: 0 1px 3px -2px #121212, inset 0 2px 5px 0 rgba(255, 255, 255, 0.3); -webkit-box-shadow: 0 1px 3px -2px #121212, inset 0 2px 5px 0 rgba(255, 255, 255, 0.3); cursor: default; }
|
||||
|
||||
.drawing-board-controls { margin: 0 auto; text-align: center; font-size: 0; display: table; border-spacing: 9.33333px 0; position: relative; min-height: 28px; }
|
||||
.drawing-board-controls[data-align="left"] { margin: 0; left: -9.33333px; }
|
||||
.drawing-board-controls[data-align="right"] { margin: 0 0 0 auto; right: -9.33333px; }
|
||||
.drawing-board-canvas-wrapper + .drawing-board-controls, .drawing-board-controls + .drawing-board-canvas-wrapper { margin-top: 5px; }
|
||||
|
||||
.drawing-board-controls-hidden { height: 0; min-height: 0; padding: 0; margin: 0; border: 0; }
|
||||
|
||||
.drawing-board-control { display: table-cell; border-collapse: separate; vertical-align: middle; font-size: 16px; height: 100%; }
|
||||
|
||||
.drawing-board-control-inner { position: relative; height: 100%; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; }
|
||||
|
||||
.drawing-board-control > button { margin: 0; vertical-align: middle; }
|
||||
|
||||
.drawing-board-control-colors { font-size: 0; line-height: 0; }
|
||||
|
||||
.drawing-board-control-colors-current { border: 1px solid #ccc; cursor: pointer; display: inline-block; width: 26px; height: 26px; }
|
||||
|
||||
.drawing-board-control-colors-rainbows { display: inline-block; margin-left: 5px; position: absolute; left: 0; top: 33px; margin-left: 0; z-index: 100; width: 250px; height: auto; padding: 4px; }
|
||||
|
||||
.drawing-board-control-colors-rainbow { height: 18px; }
|
||||
|
||||
.drawing-board-control-colors-picker:first-child { margin-right: 5px; }
|
||||
|
||||
.drawing-board-control-colors-picker { display: inline-block; width: 18px; height: 18px; cursor: pointer; }
|
||||
|
||||
.drawing-board-control-colors-picker[data-color="rgba(255, 255, 255, 1)"] { width: 16px; height: 17px; border: 1px solid #ccc; border-bottom: none; }
|
||||
|
||||
.drawing-board-control-colors-picker:hover { width: 16px; height: 16px; border: 1px solid #555; }
|
||||
|
||||
.drawing-board-control-drawingmode > button { margin-right: 2px; }
|
||||
.drawing-board-control-drawingmode > button:last-child { margin-right: 0; }
|
||||
|
||||
.drawing-board-control-drawingmode-pencil-button { overflow: hidden; *text-indent: -9999px; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAe9JREFUeNpiZAACVlFRBhYREQZcQPnbNwa3N28YlL5+ZfgLFfvPwGD9m4FhIgsDHuAO0gTUDNKIBvyBmqt/MTDMY8Gl0f31azD7L6oUIxCnAzWmAPHBfwwM01AMUAV6JfPQIVwOYgVqqPnFyOjz6///O38YGKpAgmAD1OXlGdTk5PD5hgeouZudj8/uy9evP/78/dsFFPsJNiAoKIiBABAHap4oLi9v8fTNm48//v7NBwbgWZgkE7rqt8DY+A8JZRBW+cfIuEDT0NDlzadP3z98/doPFDuCrB7TAGFhBqCNIGwM9OcKUzs7+xdv3355+f79VqDYAiTDwZgJh7ONgYpnOvn4GL949erT7UePdgL5JVCD4fgBLBBxaX74+PG789evnwby0/8jKXgExIeB+CG6Af///1e9Ki9vFSAkZPzoyZPPJy9evA9MB77/sWiEARZkzV+/fvXYtGnTpG3btj28EBT0BqjZ5D8OjXCwPksUhA1Wpggf/PHjx/9169Y9EBERaUlgZmaIAcrLE4rk5sIqBqDmlefnRPzfWGX5EaSZm5ubgRloADGA5QZ3RgK7gESY4PMNn9ZtObPpzZvfU4DiYkiB/RcHG+S7fyxAMH/lFU2GOZd2bLx18/cEUMoD4j9I+DcS/RtJHGTYf4AAAwAxaOMYHjxKFwAAAABJRU5ErkJggg=='); background-position: 50% 50%; background-repeat: no-repeat; }
|
||||
.drawing-board-control-drawingmode-pencil-button:before { content: ""; display: block; width: 0; height: 100%; }
|
||||
|
||||
.drawing-board-control-drawingmode-eraser-button { overflow: hidden; *text-indent: -9999px; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAkpJREFUeNp0kk+IElEcx39vFBc9+OfQRTAwzFt4CaYOKStj6MoeculStzoIQSB4kCVckmDx4iGCXWYJIqjoVOzO1l4qT1F7WfBWHvxzDPyTB3XUmXn93suRybUffHmP997n9/cRsFgwGARJkiAcDsPlwgEIeEZQAhCRAkgAlOD6SQP4rgMFDWVnYCAQgFgsBqFQCBwOByzZNQOotPHx1RNCCCipu6bfb+zSnslkeOQVILPrBkAirbws9btdTEWAzZPXpfepOzaeGMBXwe/3w3+MwTc3Dl+UeghTiskbBvR6Pbh18mZHB0jjmxvCKhIfR37s3r+Sevf8ca/T4TBF2HTSODuDxP7uNjrZFFbBk8lEzOVyspa4ykGYw2zfbTb/7ilvok1YhlVVFfP5vDydTkHXdXDdlhZOOnPY4/HA0YPtp3h6LFjh8XgsFgoFGTPgsKm1zDr8ajTQh8Fh5eGjZzjGI8yjKlgjF4tFGdd/YKYmRja24hw+zu3sYe2HiH3hYzQjl8tleTQanWtou93G6Qngdrth6+1+9h6hTULJZ/PeziJXKhV5OByeg1ut1gJOp9NZTdNOcQ419ot+ggp1qoLdBFmqVmNpm3A8Huewy+Wq1RH8QH9zmBlJJpMRdCIqiiIPBgN+2MCGsW/r8/kgGo1m0fmpzWarseayHlmNeL1eFiWC0cRqtSr3+/3FpSiKHMZtjU1glbFyfKgLTqfzEka9OJvNeDnzz1JnCaFmqOl8ZdJY1SiDOXCiXKg1NtG5DIt0y6ov3dE/AgwAENFWYYLj4mYAAAAASUVORK5CYII='); background-position: 50% 50%; background-repeat: no-repeat; }
|
||||
.drawing-board-control-drawingmode-eraser-button:before { content: ""; display: block; width: 0; height: 100%; }
|
||||
|
||||
.drawing-board-control-drawingmode-filler-button { overflow: hidden; *text-indent: -9999px; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAnNJREFUeNp0k0trE1EUx89MJpNJooYYXBgDNtCKdRPwlbqoCKUtaNVNA0Uo7UbMxoVPEARTXEi+QWfnwn6DEAlEkrSLttTGRiULEQlJ8yChmbzI++E50yTUJA78uMy953/u/557LmOz2WDEZ2m1WrckSRJSqdR2tVrdHQyYebwHtVoNuFHqTqczhQnWKaBYLDoKhcIuzgHDMKBSqeD20qd+LNdsNocSoFhRr9ctpVLJigl4xIIJQizLAmG4cAPa7bYcy9Iug5TL5UYikbD6/X7Rbre/IUcYe3WUW5ZsnQQzW9LpNOPz+UQc5aBM5mgdh7vI9FCCAesW2tnr9YqZTAby+bw8f3AQRP6853n+Ph5hemSCntjj8YjZbFYWx2IxeS2RSEMwuA87O79eqdXquVolK+GxnP0EPbHb7RZJSGABIR6PA11zJHKIR2MhHA5DIPDj7eH3j95KpfK60Wg8Yntil8slkqgnpioLghacTidoNDpEC3q9HnheCc3s1jZeLcW943pirPw/4lKpBkqlDubnl/riycnLsLy88EKj0fhzuRyZv8RFo1E6wpBYkiqy7Z54YmIcVlYeyOKC4mYwJ0nHRaQuM5vNT6hB/iceG7sIq6sPnwmC4MerDkby40AOCCoiddie1Wp92W7zQ2KTyQSLizNP8T0EsPLBbxEDnCj0GkM2qIEwyZRCobizsfH5A1ZXFhuN52F29vpz3HkL574mk8lj24Y5wsHkvjjoX0BOIWc5jruHzbK2ufmzEwpFO3jnDhQv4JoROYdoERVyGjEgZ8iBDlF3FzXo4go6utZ9lftY4N/dXisjR0i1G0ublv8KMAA0ZoUlicxrhwAAAABJRU5ErkJggg=='); background-position: 50% 50%; background-repeat: no-repeat; }
|
||||
.drawing-board-control-drawingmode-filler-button:before { content: ""; display: block; width: 0; height: 100%; }
|
||||
|
||||
.drawing-board-control-navigation > button { font-family: Helvetica, Arial, sans-serif; font-size: 14px; font-weight: bold; margin-right: 2px; }
|
||||
.drawing-board-control-navigation > button:last-child { margin-right: 0; }
|
||||
|
||||
.drawing-board-control-size[data-drawing-board-type="range"] .drawing-board-control-inner { width: 75px; }
|
||||
.drawing-board-control-size[data-drawing-board-type="dropdown"] .drawing-board-control-inner { overflow: visible; }
|
||||
|
||||
.drawing-board-control-size-range-input { position: relative; width: 100%; z-index: 100; margin: 0; padding: 0; border: 0; }
|
||||
|
||||
.drawing-board-control-size-range-current, .drawing-board-control-size-dropdown-current span, .drawing-board-control-size-dropdown span { display: block; background: #333; opacity: .8; }
|
||||
|
||||
.drawing-board-control-size-range-current { display: inline-block; opacity: .15; position: absolute; pointer-events: none; left: 50%; top: 50%; z-index: 50; }
|
||||
|
||||
.drawing-board-control-size-dropdown-current { display: block; height: 100%; width: 40px; overflow: hidden; position: relative; }
|
||||
.drawing-board-control-size-dropdown-current span { position: absolute; left: 50%; top: 50%; }
|
||||
|
||||
.drawing-board-control-size-dropdown { position: absolute; left: -6px; top: 33px; height: auto; list-style-type: none; margin: 0; padding: 0; z-index: 100; }
|
||||
.drawing-board-control-size-dropdown li { display: block; padding: 4px; margin: 3px 0; min-height: 16px; }
|
||||
.drawing-board-control-size-dropdown li:hover { background: #ccc; }
|
||||
.drawing-board-control-size-dropdown span { margin: 0 auto; }
|
||||
|
||||
.drawing-board-control-download-button { overflow: hidden; *text-indent: -9999px; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAoBJREFUeNqMkr9PU1EUx7/vR1tQ3yu10hAmTawtBSYSy2YccFOcnDQm/gewOLnj5GYMg4sdXFxkMca4OBAwdUBe/ZkIGFp+9tHXvh/3/fTcAm01aLzJybnn3nM+95tzrnDl6Tb+sibuTmWUWj3C6/Juk+LySUmyvt0FCKKA02ryOCy6LBiu15ngMbZ5DDCNBqIw6gKM+n4nECUJru3glKry8CpjQaHVYmC2rVH82DIMMMdGGARdwJ+SPNdFS9chx+MXDNMp/NzagWNatk/nQU/hiYAoih6FYTBCBs9zUXMCbAhx2OYOv351lPOJ3EwH4LteL6Dcp/Rfu3FrstDyIizt+agpaYxNDU0M9gl4v7Ck+TYrCYLQqZHUyTtdQBiutPSGUflczSXHs5lVKwZdSOBMvwztxVvN0RtzsiyXBFHsAvL5PBSnCpXV2getILFiE2SjspYbuZzPiDSZ2vOXmlvX5yQqTmMfg9ZXqtls1wnT09OHEyAq0aFLg/gSXsSWq9wWk+p9PrCoYTwcijdLOfE7UsEufN9HGIYnT4EnTGIXe1KqtNNIvuNnGamxfi7SgQD/nIJCTbzOPQ/SQh1pud7T4M6W/8qFIw/5WAr5m7Ozsw9UVc069Fls2yJzSC5/lnc9RhaHZVnfSqUnEgXP2oBqtYqBgYG2+mKxmOVADnAcB4yxHgD1RzehKKns/LyV4gUHBweQy+UyRkdH6UKJ6fQDFxcXoWkaXJeRuTgUGCdLQJ9bx72lGZimGWs2m+083oN+2iiFQiGxvLy8RrDzudyltgrG3N8U2G8CrPz4sGYYRqJSqWR4H/jNWbJhUjAWi8XG8R/L87yPpGCVttVfAgwAVpZR+8tZC08AAAAASUVORK5CYII='); background-position: 50% 50%; background-repeat: no-repeat; }
|
||||
.drawing-board-control-download-button:before { content: ""; display: block; width: 0; height: 100%; }
|
||||
1374
mondumas/static/dist/drawingboard/drawingboard.js
vendored
Normal file
1374
mondumas/static/dist/drawingboard/drawingboard.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
5
mondumas/static/dist/drawingboard/drawingboard.min.css
vendored
Normal file
5
mondumas/static/dist/drawingboard/drawingboard.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
4
mondumas/static/dist/drawingboard/drawingboard.min.js
vendored
Normal file
4
mondumas/static/dist/drawingboard/drawingboard.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
16
mondumas/static/dist/drawingboard/drawingboard.nocontrol.css
vendored
Normal file
16
mondumas/static/dist/drawingboard/drawingboard.nocontrol.css
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
/* drawingboard.js v0.4.6 - https://github.com/Leimi/drawingboard.js
|
||||
* Copyright (c) 2015 Emmanuel Pelletier
|
||||
* Licensed MIT */
|
||||
.drawing-board, .drawing-board * { -webkit-box-sizing: content-box; -moz-box-sizing: content-box; box-sizing: content-box; }
|
||||
|
||||
.drawing-board-utils-hidden { display: none !important; }
|
||||
|
||||
.drawing-board { position: relative; display: block; }
|
||||
|
||||
.drawing-board-canvas-wrapper { position: relative; margin: 0; border: 1px solid #ddd; }
|
||||
|
||||
.drawing-board-canvas { position: absolute; top: 0; left: 0; z-index: 10; width: auto; }
|
||||
|
||||
.drawing-board-canvas { cursor: crosshair; z-index: 20; }
|
||||
|
||||
.drawing-board-cursor { position: absolute; top: 0; left: 0; pointer-events: none; border-radius: 50%; background: #ccc; background: rgba(0, 0, 0, 0.2); z-index: 30; }
|
||||
971
mondumas/static/dist/drawingboard/drawingboard.nocontrol.js
vendored
Normal file
971
mondumas/static/dist/drawingboard/drawingboard.nocontrol.js
vendored
Normal file
@@ -0,0 +1,971 @@
|
||||
/* drawingboard.js v0.4.6 - https://github.com/Leimi/drawingboard.js
|
||||
* Copyright (c) 2015 Emmanuel Pelletier
|
||||
* Licensed MIT */
|
||||
(function() {
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* SimpleUndo is a very basic javascript undo/redo stack for managing histories of basically anything.
|
||||
*
|
||||
* options are: {
|
||||
* * `provider` : required. a function to call on `save`, which should provide the current state of the historized object through the given "done" callback
|
||||
* * `maxLength` : the maximum number of items in history
|
||||
* * `opUpdate` : a function to call to notify of changes in history. Will be called on `save`, `undo`, `redo` and `clear`
|
||||
* }
|
||||
*
|
||||
*/
|
||||
var SimpleUndo = function(options) {
|
||||
|
||||
var settings = options ? options : {};
|
||||
var defaultOptions = {
|
||||
provider: function() {
|
||||
throw new Error("No provider!");
|
||||
},
|
||||
maxLength: 30,
|
||||
onUpdate: function() {}
|
||||
};
|
||||
|
||||
this.provider = (typeof settings.provider != 'undefined') ? settings.provider : defaultOptions.provider;
|
||||
this.maxLength = (typeof settings.maxLength != 'undefined') ? settings.maxLength : defaultOptions.maxLength;
|
||||
this.onUpdate = (typeof settings.onUpdate != 'undefined') ? settings.onUpdate : defaultOptions.onUpdate;
|
||||
|
||||
this.initialItem = null;
|
||||
this.clear();
|
||||
};
|
||||
|
||||
function truncate (stack, limit) {
|
||||
while (stack.length > limit) {
|
||||
stack.shift();
|
||||
}
|
||||
}
|
||||
|
||||
SimpleUndo.prototype.initialize = function(initialItem) {
|
||||
this.stack[0] = initialItem;
|
||||
this.initialItem = initialItem;
|
||||
};
|
||||
|
||||
|
||||
SimpleUndo.prototype.clear = function() {
|
||||
this.stack = [this.initialItem];
|
||||
this.position = 0;
|
||||
this.onUpdate();
|
||||
};
|
||||
|
||||
SimpleUndo.prototype.save = function() {
|
||||
this.provider(function(current) {
|
||||
truncate(this.stack, this.maxLength);
|
||||
this.position = Math.min(this.position,this.stack.length - 1);
|
||||
|
||||
this.stack = this.stack.slice(0, this.position + 1);
|
||||
this.stack.push(current);
|
||||
this.position++;
|
||||
this.onUpdate();
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
SimpleUndo.prototype.undo = function(callback) {
|
||||
if (this.canUndo()) {
|
||||
var item = this.stack[--this.position];
|
||||
this.onUpdate();
|
||||
|
||||
if (callback) {
|
||||
callback(item);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
SimpleUndo.prototype.redo = function(callback) {
|
||||
if (this.canRedo()) {
|
||||
var item = this.stack[++this.position];
|
||||
this.onUpdate();
|
||||
|
||||
if (callback) {
|
||||
callback(item);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
SimpleUndo.prototype.canUndo = function() {
|
||||
return this.position > 0;
|
||||
};
|
||||
|
||||
SimpleUndo.prototype.canRedo = function() {
|
||||
return this.position < this.count();
|
||||
};
|
||||
|
||||
SimpleUndo.prototype.count = function() {
|
||||
return this.stack.length - 1; // -1 because of initial item
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//exports
|
||||
// node module
|
||||
if (typeof module != 'undefined') {
|
||||
module.exports = SimpleUndo;
|
||||
}
|
||||
|
||||
// browser global
|
||||
if (typeof window != 'undefined') {
|
||||
window.SimpleUndo = SimpleUndo;
|
||||
}
|
||||
|
||||
})();
|
||||
window.DrawingBoard = typeof DrawingBoard !== "undefined" ? DrawingBoard : {};
|
||||
|
||||
|
||||
DrawingBoard.Utils = {};
|
||||
|
||||
/*!
|
||||
* Tim (lite)
|
||||
* github.com/premasagar/tim
|
||||
*//*
|
||||
A tiny, secure JavaScript micro-templating script.
|
||||
*/
|
||||
DrawingBoard.Utils.tpl = (function(){
|
||||
"use strict";
|
||||
|
||||
var start = "{{",
|
||||
end = "}}",
|
||||
path = "[a-z0-9_][\\.a-z0-9_]*", // e.g. config.person.name
|
||||
pattern = new RegExp(start + "\\s*("+ path +")\\s*" + end, "gi"),
|
||||
undef;
|
||||
|
||||
return function(template, data){
|
||||
// Merge data into the template string
|
||||
return template.replace(pattern, function(tag, token){
|
||||
var path = token.split("."),
|
||||
len = path.length,
|
||||
lookup = data,
|
||||
i = 0;
|
||||
|
||||
for (; i < len; i++){
|
||||
lookup = lookup[path[i]];
|
||||
|
||||
// Property not found
|
||||
if (lookup === undef){
|
||||
throw "tim: '" + path[i] + "' not found in " + tag;
|
||||
}
|
||||
|
||||
// Return the required value
|
||||
if (i === len - 1){
|
||||
return lookup;
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
}());
|
||||
|
||||
/**
|
||||
* https://github.com/jeromeetienne/microevent.js
|
||||
* MicroEvent - to make any js object an event emitter (server or browser)
|
||||
*
|
||||
* - pure javascript - server compatible, browser compatible
|
||||
* - dont rely on the browser doms
|
||||
* - super simple - you get it immediatly, no mistery, no magic involved
|
||||
*
|
||||
* - create a MicroEventDebug with goodies to debug
|
||||
* - make it safer to use
|
||||
*/
|
||||
DrawingBoard.Utils.MicroEvent = function(){};
|
||||
|
||||
DrawingBoard.Utils.MicroEvent.prototype = {
|
||||
bind : function(event, fct){
|
||||
this._events = this._events || {};
|
||||
this._events[event] = this._events[event] || [];
|
||||
this._events[event].push(fct);
|
||||
},
|
||||
unbind : function(event, fct){
|
||||
this._events = this._events || {};
|
||||
if( event in this._events === false ) return;
|
||||
this._events[event].splice(this._events[event].indexOf(fct), 1);
|
||||
},
|
||||
trigger : function(event /* , args... */){
|
||||
this._events = this._events || {};
|
||||
if( event in this._events === false ) return;
|
||||
for(var i = 0; i < this._events[event].length; i++){
|
||||
this._events[event][i].apply(this, Array.prototype.slice.call(arguments, 1));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//I know.
|
||||
DrawingBoard.Utils._boxBorderSize = function($el, withPadding, withMargin, direction) {
|
||||
withPadding = !!withPadding || true;
|
||||
withMargin = !!withMargin || false;
|
||||
var width = 0,
|
||||
props;
|
||||
if (direction == "width") {
|
||||
props = ['border-left-width', 'border-right-width'];
|
||||
if (withPadding) props.push('padding-left', 'padding-right');
|
||||
if (withMargin) props.push('margin-left', 'margin-right');
|
||||
} else {
|
||||
props = ['border-top-width', 'border-bottom-width'];
|
||||
if (withPadding) props.push('padding-top', 'padding-bottom');
|
||||
if (withMargin) props.push('margin-top', 'margin-bottom');
|
||||
}
|
||||
for (var i = props.length - 1; i >= 0; i--)
|
||||
width += parseInt($el.css(props[i]).replace('px', ''), 10);
|
||||
return width;
|
||||
};
|
||||
|
||||
DrawingBoard.Utils.boxBorderWidth = function($el, withPadding, withMargin) {
|
||||
return DrawingBoard.Utils._boxBorderSize($el, withPadding, withMargin, 'width');
|
||||
};
|
||||
|
||||
DrawingBoard.Utils.boxBorderHeight = function($el, withPadding, withMargin) {
|
||||
return DrawingBoard.Utils._boxBorderSize($el, withPadding, withMargin, 'height');
|
||||
};
|
||||
|
||||
DrawingBoard.Utils.isColor = function(string) {
|
||||
if (!string || !string.length) return false;
|
||||
return (/(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i).test(string) || $.inArray(string.substring(0, 3), ['rgb', 'hsl']) !== -1;
|
||||
};
|
||||
|
||||
/**
|
||||
* Packs an RGB color into a single integer.
|
||||
*/
|
||||
DrawingBoard.Utils.RGBToInt = function(r, g, b) {
|
||||
var c = 0;
|
||||
c |= (r & 255) << 16;
|
||||
c |= (g & 255) << 8;
|
||||
c |= (b & 255);
|
||||
return c;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns informations on the pixel located at (x,y).
|
||||
*/
|
||||
DrawingBoard.Utils.pixelAt = function(image, x, y) {
|
||||
var i = (y * image.width + x) * 4;
|
||||
var c = DrawingBoard.Utils.RGBToInt(
|
||||
image.data[i],
|
||||
image.data[i + 1],
|
||||
image.data[i + 2]
|
||||
);
|
||||
|
||||
return [
|
||||
i, // INDEX
|
||||
x, // X
|
||||
y, // Y
|
||||
c // COLOR
|
||||
];
|
||||
};
|
||||
|
||||
/**
|
||||
* Compares two colors with the given tolerance (between 0 and 255).
|
||||
*/
|
||||
DrawingBoard.Utils.compareColors = function(a, b, tolerance) {
|
||||
if (tolerance === 0) {
|
||||
return (a === b);
|
||||
}
|
||||
|
||||
var ra = (a >> 16) & 255, rb = (b >> 16) & 255,
|
||||
ga = (a >> 8) & 255, gb = (b >> 8) & 255,
|
||||
ba = a & 255, bb = b & 255;
|
||||
|
||||
return (Math.abs(ra - rb) <= tolerance)
|
||||
&& (Math.abs(ga - gb) <= tolerance)
|
||||
&& (Math.abs(ba - bb) <= tolerance);
|
||||
};
|
||||
|
||||
(function() {
|
||||
var lastTime = 0;
|
||||
var vendors = ['ms', 'moz', 'webkit', 'o'];
|
||||
for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
|
||||
window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];
|
||||
window.cancelAnimationFrame = window[vendors[x]+'CancelAnimationFrame'] || window[vendors[x]+'CancelRequestAnimationFrame'];
|
||||
}
|
||||
}());
|
||||
|
||||
window.DrawingBoard = typeof DrawingBoard !== "undefined" ? DrawingBoard : {};
|
||||
|
||||
/**
|
||||
* pass the id of the html element to put the drawing board into
|
||||
* and some options : {
|
||||
* controls: array of controls to initialize with the drawingboard. 'Colors', 'Size', and 'Navigation' by default
|
||||
* instead of simple strings, you can pass an object to define a control opts
|
||||
* ie ['Color', { Navigation: { reset: false }}]
|
||||
* controlsPosition: "top left" by default. Define where to put the controls: at the "top" or "bottom" of the canvas, aligned to "left"/"right"/"center"
|
||||
* background: background of the drawing board. Give a hex color or an image url "#ffffff" (white) by default
|
||||
* color: pencil color ("#000000" by default)
|
||||
* size: pencil size (3 by default)
|
||||
* webStorage: 'session', 'local' or false ('session' by default). store the current drawing in session or local storage and restore it when you come back
|
||||
* droppable: true or false (false by default). If true, dropping an image on the canvas will include it and allow you to draw on it,
|
||||
* errorMessage: html string to put in the board's element on browsers that don't support canvas.
|
||||
* stretchImg: default behavior of image setting on the canvas: set to the canvas width/height or not? false by default
|
||||
* }
|
||||
*/
|
||||
DrawingBoard.Board = function(id, opts) {
|
||||
this.opts = this.mergeOptions(opts);
|
||||
|
||||
this.ev = new DrawingBoard.Utils.MicroEvent();
|
||||
|
||||
this.id = id;
|
||||
this.$el = $(document.getElementById(id));
|
||||
if (!this.$el.length)
|
||||
return false;
|
||||
|
||||
var tpl = '<div class="drawing-board-canvas-wrapper"></canvas><canvas class="drawing-board-canvas"></canvas><div class="drawing-board-cursor drawing-board-utils-hidden"></div></div>';
|
||||
if (this.opts.controlsPosition.indexOf("bottom") > -1) tpl += '<div class="drawing-board-controls"></div>';
|
||||
else tpl = '<div class="drawing-board-controls"></div>' + tpl;
|
||||
|
||||
this.$el.addClass('drawing-board').append(tpl);
|
||||
this.dom = {
|
||||
$canvasWrapper: this.$el.find('.drawing-board-canvas-wrapper'),
|
||||
$canvas: this.$el.find('.drawing-board-canvas'),
|
||||
$cursor: this.$el.find('.drawing-board-cursor'),
|
||||
$controls: this.$el.find('.drawing-board-controls')
|
||||
};
|
||||
|
||||
$.each(['left', 'right', 'center'], $.proxy(function(n, val) {
|
||||
if (this.opts.controlsPosition.indexOf(val) > -1) {
|
||||
this.dom.$controls.attr('data-align', val);
|
||||
return false;
|
||||
}
|
||||
}, this));
|
||||
|
||||
this.canvas = this.dom.$canvas.get(0);
|
||||
this.ctx = this.canvas && this.canvas.getContext && this.canvas.getContext('2d') ? this.canvas.getContext('2d') : null;
|
||||
this.color = this.opts.color;
|
||||
|
||||
if (!this.ctx) {
|
||||
if (this.opts.errorMessage)
|
||||
this.$el.html(this.opts.errorMessage);
|
||||
return false;
|
||||
}
|
||||
|
||||
this.storage = this._getStorage();
|
||||
|
||||
this.initHistory();
|
||||
//init default board values before controls are added (mostly pencil color and size)
|
||||
this.reset({ webStorage: false, history: false, background: false });
|
||||
//init controls (they will need the default board values to work like pencil color and size)
|
||||
this.initControls();
|
||||
//set board's size after the controls div is added
|
||||
this.resize();
|
||||
//reset the board to take all resized space
|
||||
this.reset({ webStorage: false, history: false, background: true });
|
||||
this.restoreWebStorage();
|
||||
this.initDropEvents();
|
||||
this.initDrawEvents();
|
||||
};
|
||||
|
||||
|
||||
|
||||
DrawingBoard.Board.defaultOpts = {
|
||||
controls: ['Color', 'DrawingMode', 'Size', 'Navigation'],
|
||||
controlsPosition: "top left",
|
||||
color: "#000000",
|
||||
size: 1,
|
||||
background: "#fff",
|
||||
eraserColor: "background",
|
||||
fillTolerance: 100,
|
||||
fillHack: true, //try to prevent issues with anti-aliasing with a little hack by default
|
||||
webStorage: 'session',
|
||||
droppable: false,
|
||||
enlargeYourContainer: false,
|
||||
errorMessage: "<p>It seems you use an obsolete browser. <a href=\"http://browsehappy.com/\" target=\"_blank\">Update it</a> to start drawing.</p>",
|
||||
stretchImg: false //when setting the canvas img, strech the image at the whole canvas size when this opt is true
|
||||
};
|
||||
|
||||
|
||||
|
||||
DrawingBoard.Board.prototype = {
|
||||
|
||||
mergeOptions: function(opts) {
|
||||
opts = $.extend({}, DrawingBoard.Board.defaultOpts, opts);
|
||||
if (!opts.background && opts.eraserColor === "background")
|
||||
opts.eraserColor = "transparent";
|
||||
return opts;
|
||||
},
|
||||
|
||||
/**
|
||||
* Canvas reset/resize methods: put back the canvas to its default values
|
||||
*
|
||||
* depending on options, can set color, size, background back to default values
|
||||
* and store the reseted canvas in webstorage and history queue
|
||||
*
|
||||
* resize values depend on the `enlargeYourContainer` option
|
||||
*/
|
||||
|
||||
reset: function(opts) {
|
||||
opts = $.extend({
|
||||
color: this.opts.color,
|
||||
size: this.opts.size,
|
||||
webStorage: true,
|
||||
history: true,
|
||||
background: false
|
||||
}, opts);
|
||||
|
||||
this.setMode('pencil');
|
||||
|
||||
if (opts.background) {
|
||||
this.resetBackground(this.opts.background, $.proxy(function() {
|
||||
if (opts.history) this.saveHistory();
|
||||
}, this));
|
||||
}
|
||||
|
||||
if (opts.color) this.setColor(opts.color);
|
||||
if (opts.size) this.ctx.lineWidth = opts.size;
|
||||
|
||||
this.ctx.lineCap = "round";
|
||||
this.ctx.lineJoin = "round";
|
||||
// this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.width);
|
||||
|
||||
if (opts.webStorage) this.saveWebStorage();
|
||||
|
||||
// if opts.background we already dealt with the history
|
||||
if (opts.history && !opts.background) this.saveHistory();
|
||||
|
||||
this.blankCanvas = this.getImg();
|
||||
|
||||
this.ev.trigger('board:reset', opts);
|
||||
},
|
||||
|
||||
resetBackground: function(background, callback) {
|
||||
background = background || this.opts.background;
|
||||
|
||||
var bgIsColor = DrawingBoard.Utils.isColor(background);
|
||||
var prevMode = this.getMode();
|
||||
this.setMode('pencil');
|
||||
this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height);
|
||||
if (bgIsColor) {
|
||||
this.ctx.fillStyle = background;
|
||||
this.ctx.fillRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height);
|
||||
this.history.initialize(this.getImg());
|
||||
if (callback) callback();
|
||||
} else if (background)
|
||||
this.setImg(background, {
|
||||
callback: $.proxy(function() {
|
||||
this.history.initialize(this.getImg());
|
||||
if (callback) callback();
|
||||
}, this)
|
||||
});
|
||||
this.setMode(prevMode);
|
||||
},
|
||||
|
||||
resize: function() {
|
||||
this.dom.$controls.toggleClass('drawing-board-controls-hidden', (!this.controls || !this.controls.length));
|
||||
|
||||
var canvasWidth, canvasHeight;
|
||||
var widths = [
|
||||
this.$el.width(),
|
||||
DrawingBoard.Utils.boxBorderWidth(this.$el),
|
||||
DrawingBoard.Utils.boxBorderWidth(this.dom.$canvasWrapper, true, true)
|
||||
];
|
||||
var heights = [
|
||||
this.$el.height(),
|
||||
DrawingBoard.Utils.boxBorderHeight(this.$el),
|
||||
this.dom.$controls.height(),
|
||||
DrawingBoard.Utils.boxBorderHeight(this.dom.$controls, false, true),
|
||||
DrawingBoard.Utils.boxBorderHeight(this.dom.$canvasWrapper, true, true)
|
||||
];
|
||||
var that = this;
|
||||
var sum = function(values, multiplier) { //make the sum of all array values
|
||||
multiplier = multiplier || 1;
|
||||
var res = values[0];
|
||||
for (var i = 1; i < values.length; i++) {
|
||||
res = res + (values[i]*multiplier);
|
||||
}
|
||||
return res;
|
||||
};
|
||||
var sub = function(values) { return sum(values, -1); }; //substract all array values from the first one
|
||||
|
||||
if (this.opts.enlargeYourContainer) {
|
||||
canvasWidth = this.$el.width();
|
||||
canvasHeight = this.$el.height();
|
||||
|
||||
this.$el.width( sum(widths) );
|
||||
this.$el.height( sum(heights) );
|
||||
} else {
|
||||
canvasWidth = sub(widths);
|
||||
canvasHeight = sub(heights);
|
||||
}
|
||||
|
||||
this.dom.$canvasWrapper.css('width', canvasWidth + 'px');
|
||||
this.dom.$canvasWrapper.css('height', canvasHeight + 'px');
|
||||
|
||||
this.dom.$canvas.css('width', canvasWidth + 'px');
|
||||
this.dom.$canvas.css('height', canvasHeight + 'px');
|
||||
|
||||
this.canvas.width = canvasWidth;
|
||||
this.canvas.height = canvasHeight;
|
||||
},
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Controls:
|
||||
* the drawing board can has various UI elements to control it.
|
||||
* one control is represented by a class in the namespace DrawingBoard.Control
|
||||
* it must have a $el property (jQuery object), representing the html element to append on the drawing board at initialization.
|
||||
*
|
||||
*/
|
||||
|
||||
initControls: function() {
|
||||
this.controls = [];
|
||||
if (!this.opts.controls.length || !DrawingBoard.Control) return false;
|
||||
for (var i = 0; i < this.opts.controls.length; i++) {
|
||||
var c = null;
|
||||
if (typeof this.opts.controls[i] == "string")
|
||||
c = new window['DrawingBoard']['Control'][this.opts.controls[i]](this);
|
||||
else if (typeof this.opts.controls[i] == "object") {
|
||||
for (var controlName in this.opts.controls[i]) break;
|
||||
c = new window['DrawingBoard']['Control'][controlName](this, this.opts.controls[i][controlName]);
|
||||
}
|
||||
if (c) {
|
||||
this.addControl(c);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
//add a new control or an existing one at the position you want in the UI
|
||||
//to add a totally new control, you can pass a string with the js class as 1st parameter and control options as 2nd ie "addControl('Navigation', { reset: false }"
|
||||
//the last parameter (2nd or 3rd depending on the situation) is always the position you want to place the control at
|
||||
addControl: function(control, optsOrPos, pos) {
|
||||
if (typeof control !== "string" && (typeof control !== "object" || !control instanceof DrawingBoard.Control))
|
||||
return false;
|
||||
|
||||
var opts = typeof optsOrPos == "object" ? optsOrPos : {};
|
||||
pos = pos ? pos*1 : (typeof optsOrPos == "number" ? optsOrPos : null);
|
||||
|
||||
if (typeof control == "string")
|
||||
control = new window['DrawingBoard']['Control'][control](this, opts);
|
||||
|
||||
if (pos)
|
||||
this.dom.$controls.children().eq(pos).before(control.$el);
|
||||
else
|
||||
this.dom.$controls.append(control.$el);
|
||||
|
||||
if (!this.controls)
|
||||
this.controls = [];
|
||||
this.controls.push(control);
|
||||
this.dom.$controls.removeClass('drawing-board-controls-hidden');
|
||||
},
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* History methods: undo and redo drawed lines
|
||||
*/
|
||||
|
||||
initHistory: function() {
|
||||
this.history = new SimpleUndo({
|
||||
maxLength: 30,
|
||||
provider: $.proxy(function(done) {
|
||||
done(this.getImg());
|
||||
}, this),
|
||||
onUpdate: $.proxy(function() {
|
||||
this.ev.trigger('historyNavigation');
|
||||
}, this)
|
||||
});
|
||||
},
|
||||
|
||||
saveHistory: function() {
|
||||
this.history.save();
|
||||
},
|
||||
|
||||
restoreHistory: function(image) {
|
||||
this.setImg(image, {
|
||||
callback: $.proxy(function() {
|
||||
this.saveWebStorage();
|
||||
}, this)
|
||||
});
|
||||
},
|
||||
|
||||
goBackInHistory: function() {
|
||||
this.history.undo($.proxy(this.restoreHistory, this));
|
||||
},
|
||||
|
||||
goForthInHistory: function() {
|
||||
this.history.redo($.proxy(this.restoreHistory, this));
|
||||
},
|
||||
|
||||
/**
|
||||
* Image methods: you can directly put an image on the canvas, get it in base64 data url or start a download
|
||||
*/
|
||||
|
||||
setImg: function(src, opts) {
|
||||
opts = $.extend({
|
||||
stretch: this.opts.stretchImg,
|
||||
callback: null
|
||||
}, opts);
|
||||
|
||||
var ctx = this.ctx;
|
||||
var img = new Image();
|
||||
var oldGCO = ctx.globalCompositeOperation;
|
||||
img.onload = function() {
|
||||
ctx.globalCompositeOperation = "source-over";
|
||||
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
|
||||
|
||||
if (opts.stretch) {
|
||||
ctx.drawImage(img, 0, 0, ctx.canvas.width, ctx.canvas.height);
|
||||
} else {
|
||||
ctx.drawImage(img, 0, 0);
|
||||
}
|
||||
|
||||
ctx.globalCompositeOperation = oldGCO;
|
||||
|
||||
if (opts.callback) {
|
||||
opts.callback();
|
||||
}
|
||||
};
|
||||
img.src = src;
|
||||
},
|
||||
|
||||
getImg: function() {
|
||||
return this.canvas.toDataURL("image/png");
|
||||
},
|
||||
|
||||
downloadImg: function() {
|
||||
var img = this.getImg();
|
||||
img = img.replace("image/png", "image/octet-stream");
|
||||
window.location.href = img;
|
||||
},
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* WebStorage handling : save and restore to local or session storage
|
||||
*/
|
||||
|
||||
saveWebStorage: function() {
|
||||
if (window[this.storage]) {
|
||||
window[this.storage].setItem('drawing-board-' + this.id, this.getImg());
|
||||
this.ev.trigger('board:save' + this.storage.charAt(0).toUpperCase() + this.storage.slice(1), this.getImg());
|
||||
}
|
||||
},
|
||||
|
||||
restoreWebStorage: function() {
|
||||
if (window[this.storage] && window[this.storage].getItem('drawing-board-' + this.id) !== null) {
|
||||
this.setImg(window[this.storage].getItem('drawing-board-' + this.id));
|
||||
this.ev.trigger('board:restore' + this.storage.charAt(0).toUpperCase() + this.storage.slice(1), window[this.storage].getItem('drawing-board-' + this.id));
|
||||
}
|
||||
},
|
||||
|
||||
clearWebStorage: function() {
|
||||
if (window[this.storage] && window[this.storage].getItem('drawing-board-' + this.id) !== null) {
|
||||
window[this.storage].removeItem('drawing-board-' + this.id);
|
||||
this.ev.trigger('board:clear' + this.storage.charAt(0).toUpperCase() + this.storage.slice(1));
|
||||
}
|
||||
},
|
||||
|
||||
_getStorage: function() {
|
||||
if (!this.opts.webStorage || !(this.opts.webStorage === 'session' || this.opts.webStorage === 'local')) return false;
|
||||
return this.opts.webStorage + 'Storage';
|
||||
},
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Drop an image on the canvas to draw on it
|
||||
*/
|
||||
|
||||
initDropEvents: function() {
|
||||
if (!this.opts.droppable)
|
||||
return false;
|
||||
|
||||
this.dom.$canvas.on('dragover dragenter drop', function(e) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
this.dom.$canvas.on('drop', $.proxy(this._onCanvasDrop, this));
|
||||
},
|
||||
|
||||
_onCanvasDrop: function(e) {
|
||||
e = e.originalEvent ? e.originalEvent : e;
|
||||
var files = e.dataTransfer.files;
|
||||
if (!files || !files.length || files[0].type.indexOf('image') == -1 || !window.FileReader)
|
||||
return false;
|
||||
var fr = new FileReader();
|
||||
fr.readAsDataURL(files[0]);
|
||||
fr.onload = $.proxy(function(ev) {
|
||||
this.setImg(ev.target.result, {
|
||||
callback: $.proxy(function() {
|
||||
this.saveHistory();
|
||||
}, this)
|
||||
});
|
||||
this.ev.trigger('board:imageDropped', ev.target.result);
|
||||
this.ev.trigger('board:userAction');
|
||||
}, this);
|
||||
},
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* set and get current drawing mode
|
||||
*
|
||||
* possible modes are "pencil" (draw normally), "eraser" (draw transparent, like, erase, you know), "filler" (paint can)
|
||||
*/
|
||||
|
||||
setMode: function(newMode, silent) {
|
||||
silent = silent || false;
|
||||
newMode = newMode || 'pencil';
|
||||
|
||||
this.ev.unbind('board:startDrawing', $.proxy(this.fill, this));
|
||||
|
||||
if (this.opts.eraserColor === "transparent")
|
||||
this.ctx.globalCompositeOperation = newMode === "eraser" ? "destination-out" : "source-over";
|
||||
else {
|
||||
if (newMode === "eraser") {
|
||||
if (this.opts.eraserColor === "background" && DrawingBoard.Utils.isColor(this.opts.background))
|
||||
this.ctx.strokeStyle = this.opts.background;
|
||||
else if (DrawingBoard.Utils.isColor(this.opts.eraserColor))
|
||||
this.ctx.strokeStyle = this.opts.eraserColor;
|
||||
} else if (!this.mode || this.mode === "eraser") {
|
||||
this.ctx.strokeStyle = this.color;
|
||||
}
|
||||
|
||||
if (newMode === "filler")
|
||||
this.ev.bind('board:startDrawing', $.proxy(this.fill, this));
|
||||
}
|
||||
this.mode = newMode;
|
||||
if (!silent)
|
||||
this.ev.trigger('board:mode', this.mode);
|
||||
},
|
||||
|
||||
getMode: function() {
|
||||
return this.mode || "pencil";
|
||||
},
|
||||
|
||||
setColor: function(color) {
|
||||
var that = this;
|
||||
color = color || this.color;
|
||||
if (!DrawingBoard.Utils.isColor(color))
|
||||
return false;
|
||||
this.color = color;
|
||||
if (this.opts.eraserColor !== "transparent" && this.mode === "eraser") {
|
||||
var setStrokeStyle = function(mode) {
|
||||
if (mode !== "eraser")
|
||||
that.strokeStyle = that.color;
|
||||
that.ev.unbind('board:mode', setStrokeStyle);
|
||||
};
|
||||
this.ev.bind('board:mode', setStrokeStyle);
|
||||
} else
|
||||
this.ctx.strokeStyle = this.color;
|
||||
},
|
||||
|
||||
/**
|
||||
* Fills an area with the current stroke color.
|
||||
*/
|
||||
fill: function(e) {
|
||||
if (this.getImg() === this.blankCanvas) {
|
||||
this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height);
|
||||
this.ctx.fillStyle = this.color;
|
||||
this.ctx.fillRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height);
|
||||
return;
|
||||
}
|
||||
|
||||
var img = this.ctx.getImageData(0, 0, this.canvas.width, this.canvas.height);
|
||||
|
||||
// constants identifying pixels components
|
||||
var INDEX = 0, X = 1, Y = 2, COLOR = 3;
|
||||
|
||||
// target color components
|
||||
var stroke = this.ctx.strokeStyle;
|
||||
var r = parseInt(stroke.substr(1, 2), 16);
|
||||
var g = parseInt(stroke.substr(3, 2), 16);
|
||||
var b = parseInt(stroke.substr(5, 2), 16);
|
||||
|
||||
// starting point
|
||||
var start = DrawingBoard.Utils.pixelAt(img, parseInt(e.coords.x, 10), parseInt(e.coords.y, 10));
|
||||
var startColor = start[COLOR];
|
||||
var tolerance = this.opts.fillTolerance;
|
||||
var useHack = this.opts.fillHack; //see https://github.com/Leimi/drawingboard.js/pull/38
|
||||
|
||||
// no need to continue if starting and target colors are the same
|
||||
if (DrawingBoard.Utils.compareColors(startColor, DrawingBoard.Utils.RGBToInt(r, g, b), tolerance))
|
||||
return;
|
||||
|
||||
// pixels to evaluate
|
||||
var queue = [start];
|
||||
|
||||
// loop vars
|
||||
var pixel, x, y;
|
||||
var maxX = img.width - 1;
|
||||
var maxY = img.height - 1;
|
||||
|
||||
function updatePixelColor(pixel) {
|
||||
img.data[pixel[INDEX]] = r;
|
||||
img.data[pixel[INDEX] + 1] = g;
|
||||
img.data[pixel[INDEX] + 2] = b;
|
||||
}
|
||||
|
||||
while ((pixel = queue.pop())) {
|
||||
if (useHack)
|
||||
updatePixelColor(pixel);
|
||||
|
||||
if (DrawingBoard.Utils.compareColors(pixel[COLOR], startColor, tolerance)) {
|
||||
if (!useHack)
|
||||
updatePixelColor(pixel);
|
||||
if (pixel[X] > 0) // west
|
||||
queue.push(DrawingBoard.Utils.pixelAt(img, pixel[X] - 1, pixel[Y]));
|
||||
if (pixel[X] < maxX) // east
|
||||
queue.push(DrawingBoard.Utils.pixelAt(img, pixel[X] + 1, pixel[Y]));
|
||||
if (pixel[Y] > 0) // north
|
||||
queue.push(DrawingBoard.Utils.pixelAt(img, pixel[X], pixel[Y] - 1));
|
||||
if (pixel[Y] < maxY) // south
|
||||
queue.push(DrawingBoard.Utils.pixelAt(img, pixel[X], pixel[Y] + 1));
|
||||
}
|
||||
}
|
||||
|
||||
this.ctx.putImageData(img, 0, 0);
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Drawing handling, with mouse or touch
|
||||
*/
|
||||
|
||||
initDrawEvents: function() {
|
||||
this.isDrawing = false;
|
||||
this.isMouseHovering = false;
|
||||
this.coords = {};
|
||||
this.coords.old = this.coords.current = this.coords.oldMid = { x: 0, y: 0 };
|
||||
|
||||
this.dom.$canvas.on('mousedown touchstart', $.proxy(function(e) {
|
||||
this._onInputStart(e, this._getInputCoords(e) );
|
||||
}, this));
|
||||
|
||||
this.dom.$canvas.on('mousemove touchmove', $.proxy(function(e) {
|
||||
this._onInputMove(e, this._getInputCoords(e) );
|
||||
}, this));
|
||||
|
||||
this.dom.$canvas.on('mousemove', $.proxy(function(e) {
|
||||
|
||||
}, this));
|
||||
|
||||
this.dom.$canvas.on('mouseup touchend', $.proxy(function(e) {
|
||||
this._onInputStop(e, this._getInputCoords(e) );
|
||||
}, this));
|
||||
|
||||
this.dom.$canvas.on('mouseover', $.proxy(function(e) {
|
||||
this._onMouseOver(e, this._getInputCoords(e) );
|
||||
}, this));
|
||||
|
||||
this.dom.$canvas.on('mouseout', $.proxy(function(e) {
|
||||
this._onMouseOut(e, this._getInputCoords(e) );
|
||||
|
||||
}, this));
|
||||
|
||||
$('body').on('mouseup touchend', $.proxy(function(e) {
|
||||
this.isDrawing = false;
|
||||
}, this));
|
||||
|
||||
if (window.requestAnimationFrame) requestAnimationFrame( $.proxy(this.draw, this) );
|
||||
},
|
||||
|
||||
draw: function() {
|
||||
//if the pencil size is big (>10), the small crosshair makes a friend: a circle of the size of the pencil
|
||||
//todo: have the circle works on every browser - it currently should be added only when CSS pointer-events are supported
|
||||
//we assume that if requestAnimationFrame is supported, pointer-events is too, but this is terribad.
|
||||
if (window.requestAnimationFrame && this.ctx.lineWidth > 10 && this.isMouseHovering) {
|
||||
this.dom.$cursor.css({ width: this.ctx.lineWidth + 'px', height: this.ctx.lineWidth + 'px' });
|
||||
var transform = DrawingBoard.Utils.tpl("translateX({{x}}px) translateY({{y}}px)", { x: this.coords.current.x-(this.ctx.lineWidth/2), y: this.coords.current.y-(this.ctx.lineWidth/2) });
|
||||
this.dom.$cursor.css({ 'transform': transform, '-webkit-transform': transform, '-ms-transform': transform });
|
||||
this.dom.$cursor.removeClass('drawing-board-utils-hidden');
|
||||
} else {
|
||||
this.dom.$cursor.addClass('drawing-board-utils-hidden');
|
||||
}
|
||||
|
||||
if (this.isDrawing) {
|
||||
var currentMid = this._getMidInputCoords(this.coords.current);
|
||||
this.ctx.beginPath();
|
||||
this.ctx.moveTo(currentMid.x, currentMid.y);
|
||||
this.ctx.quadraticCurveTo(this.coords.old.x, this.coords.old.y, this.coords.oldMid.x, this.coords.oldMid.y);
|
||||
this.ctx.stroke();
|
||||
|
||||
this.coords.old = this.coords.current;
|
||||
this.coords.oldMid = currentMid;
|
||||
}
|
||||
|
||||
if (window.requestAnimationFrame) requestAnimationFrame( $.proxy(function() { this.draw(); }, this) );
|
||||
},
|
||||
|
||||
_onInputStart: function(e, coords) {
|
||||
this.coords.current = this.coords.old = coords;
|
||||
this.coords.oldMid = this._getMidInputCoords(coords);
|
||||
this.isDrawing = true;
|
||||
|
||||
if (!window.requestAnimationFrame) this.draw();
|
||||
|
||||
this.ev.trigger('board:startDrawing', {e: e, coords: coords});
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
},
|
||||
|
||||
_onInputMove: function(e, coords) {
|
||||
this.coords.current = coords;
|
||||
this.ev.trigger('board:drawing', {e: e, coords: coords});
|
||||
|
||||
if (!window.requestAnimationFrame) this.draw();
|
||||
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
},
|
||||
|
||||
_onInputStop: function(e, coords) {
|
||||
if (this.isDrawing && (!e.touches || e.touches.length === 0)) {
|
||||
this.isDrawing = false;
|
||||
|
||||
this.saveWebStorage();
|
||||
this.saveHistory();
|
||||
|
||||
this.ev.trigger('board:stopDrawing', {e: e, coords: coords});
|
||||
this.ev.trigger('board:userAction');
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
}
|
||||
},
|
||||
|
||||
_onMouseOver: function(e, coords) {
|
||||
this.isMouseHovering = true;
|
||||
this.coords.old = this._getInputCoords(e);
|
||||
this.coords.oldMid = this._getMidInputCoords(this.coords.old);
|
||||
|
||||
this.ev.trigger('board:mouseOver', {e: e, coords: coords});
|
||||
},
|
||||
|
||||
_onMouseOut: function(e, coords) {
|
||||
this.isMouseHovering = false;
|
||||
|
||||
this.ev.trigger('board:mouseOut', {e: e, coords: coords});
|
||||
},
|
||||
|
||||
_getInputCoords: function(e) {
|
||||
e = e.originalEvent ? e.originalEvent : e;
|
||||
var
|
||||
rect = this.canvas.getBoundingClientRect(),
|
||||
width = this.dom.$canvas.width(),
|
||||
height = this.dom.$canvas.height()
|
||||
;
|
||||
var x, y;
|
||||
if (e.touches && e.touches.length == 1) {
|
||||
x = e.touches[0].pageX;
|
||||
y = e.touches[0].pageY;
|
||||
} else {
|
||||
x = e.pageX;
|
||||
y = e.pageY;
|
||||
}
|
||||
x = x - this.dom.$canvas.offset().left;
|
||||
y = y - this.dom.$canvas.offset().top;
|
||||
x *= (width / rect.width);
|
||||
y *= (height / rect.height);
|
||||
return {
|
||||
x: x,
|
||||
y: y
|
||||
};
|
||||
},
|
||||
|
||||
_getMidInputCoords: function(coords) {
|
||||
return {
|
||||
x: this.coords.old.x + coords.x>>1,
|
||||
y: this.coords.old.y + coords.y>>1
|
||||
};
|
||||
}
|
||||
};
|
||||
5
mondumas/static/dist/drawingboard/drawingboard.nocontrol.min.css
vendored
Normal file
5
mondumas/static/dist/drawingboard/drawingboard.nocontrol.min.css
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
/* drawingboard.js v0.4.6 - https://github.com/Leimi/drawingboard.js
|
||||
* Copyright (c) 2015 Emmanuel Pelletier
|
||||
* Licensed MIT */
|
||||
|
||||
.drawing-board,.drawing-board *{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}.drawing-board-utils-hidden{display:none!important}.drawing-board{position:relative;display:block}.drawing-board-canvas-wrapper{position:relative;margin:0;border:1px solid #ddd}.drawing-board-canvas{position:absolute;top:0;left:0;width:auto;cursor:crosshair;z-index:20}.drawing-board-cursor{position:absolute;top:0;left:0;pointer-events:none;border-radius:50%;background:#ccc;background:rgba(0,0,0,.2);z-index:30}
|
||||
4
mondumas/static/dist/drawingboard/drawingboard.nocontrol.min.js
vendored
Normal file
4
mondumas/static/dist/drawingboard/drawingboard.nocontrol.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -8,7 +8,7 @@
|
||||
<form method="POST" id="frm" class="form-horizontal">
|
||||
<div class="form-group">
|
||||
<div class="col-xs-4">
|
||||
<select class="form-control" id="agenda" name="agenda" onChange="$('#frm').submit()" tal:condition="access > 0">
|
||||
<select class="form-control" id="agenda" name="agenda" onChange="$('#frm').submit()" tal:condition="access > 1">
|
||||
<div tal:repeat="item agendas">
|
||||
<option value="${item.CD_UTI}" tal:attributes="selected agenda==item.CD_UTI and 'selected' or None">${item.NOM}</option>
|
||||
</div>
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
<div class="col-sm-offset-3 col-sm-9">
|
||||
<a class="btn btn-default" href="/agenda/${rdv.rdv_debut.strftime('%Y-%m-%d')}">
|
||||
<span class="glyphicon glyphicon-arrow-left"></span> Annuler</a>
|
||||
<button class="btn btn-primary" type="submit" name="form.submitted">
|
||||
<button class="btn btn-primary" type="submit" name="form.submitted" tal:condition="access > 1">
|
||||
<span class="glyphicon glyphicon-ok"></span> Enregistrer</button>
|
||||
<button class="btn btn-danger" type="submit" name="form.deleted"
|
||||
tal:condition="nolig != '0' and logged_in.upper()==rdv.USERMAJ.upper()">
|
||||
|
||||
@@ -1,60 +1,153 @@
|
||||
<metal:block use-macro="main_template">
|
||||
<div metal:fill-slot="content">
|
||||
|
||||
<div class="container-fluid text-center">
|
||||
<br />
|
||||
<!-- row 1 : MENU GENERAL -->
|
||||
<div class="row">
|
||||
<div class="col-sm-3">
|
||||
<a href="${request.application_url}/agenda/today">
|
||||
<span class="glyphicon glyphicon-calendar logo-small"></span><br />
|
||||
<h4>MON AGENDA</h4></a>
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
<a href="${request.application_url}/rdf_list">
|
||||
<span class="glyphicon glyphicon-tint logo-success"></span>
|
||||
<h4>RAPPORTS RDF</h4></a>
|
||||
</div>
|
||||
<div class="col-sm-3" tal:condition="logged_in=='CAO'">
|
||||
<a href="${request.application_url}/devis_list">
|
||||
<span class="glyphicon glyphicon-text-height logo-small"></span>
|
||||
<h4>E-DEVIS</h4></a>
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
<a href="${request.application_url}/dossier_lookup">
|
||||
<span class="glyphicon glyphicon-search logo-small"></span>
|
||||
<h4>RECH. DOSSIER</h4></a>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<!-- row 2 : MENU GESTIONNAIRE -->
|
||||
<div class="row" tal:condition="access >= 5">
|
||||
<div class="col-sm-3">
|
||||
<a href="${request.application_url}/planning/today">
|
||||
<span class="glyphicon glyphicon-calendar logo-small"></span><br />
|
||||
<h4>PLANNING</h4></a>
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
<a href="${request.application_url}/demandes" tal:condition="access > 0">
|
||||
<span class="glyphicon glyphicon-download-alt logo-warning"></span>
|
||||
<h4>EMAILS <span class="glyphicon glyphicon-arrow-right"></span> DOSSIERS</h4></a>
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
<a href="${request.application_url}/upload_om" tal:condition="access > 0">
|
||||
<span class="glyphicon glyphicon-download-alt logo-warning"></span>
|
||||
<h4>PDF <span class="glyphicon glyphicon-arrow-right"></span> DOSSIERS</h4></a>
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
<a href="${request.application_url}/stats" tal:condition="access > 6">
|
||||
<span class="glyphicon glyphicon-stats logo-warning"></span>
|
||||
<h4>STATISTIQUES</h4></a>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
</div>
|
||||
|
||||
</div><!-- content -->
|
||||
</metal:block>
|
||||
|
||||
<metal:block use-macro="main_template">
|
||||
<div metal:fill-slot="content">
|
||||
|
||||
<div class="container-fluid">
|
||||
<br />
|
||||
<div class="row">
|
||||
<div class="col-xs-4">
|
||||
<a href="${request.application_url}/agenda/today">
|
||||
<div class="info-box bg-prod">
|
||||
<span class="info-box-icon"><i class="glyphicon glyphicon-calendar"></i></span>
|
||||
<div class="info-box-content">
|
||||
<span class="info-box-number">AGENDA</span>
|
||||
<span class="info-box-text">RDV JOUR : <span class="badge bg-red">${nb_rdv}</span></span>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-xs-4" tal:condition="access != 1">
|
||||
<a href="${request.application_url}/rdf_list">
|
||||
<div class="info-box bg-green">
|
||||
<span class="info-box-icon"><i class="glyphicon glyphicon-tint"></i></span>
|
||||
<div class="info-box-content">
|
||||
<span class="info-box-number">RDF</span>
|
||||
<span class="info-box-text">À VALIDER : <span class="badge bg-red">${nb_rdf}</span></span>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-xs-4">
|
||||
<a href="${request.application_url}/dossier_lookup" tal:condition="access > 0">
|
||||
<div class="info-box bg-prod">
|
||||
<span class="info-box-icon"><i class="glyphicon glyphicon-search"></i></span>
|
||||
<div class="info-box-content">
|
||||
<span class="info-box-number">RECHERCHE DOSSIER</span>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
|
||||
<div class="row" tal:condition="access >= 5">
|
||||
<div class="col-xs-4">
|
||||
<a href="${request.application_url}/planning/today">
|
||||
<div class="info-box bg-gest">
|
||||
<span class="info-box-icon"><i class="glyphicon glyphicon-calendar"></i></span>
|
||||
<div class="info-box-content">
|
||||
<span class="info-box-number">PLANNING</span>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-xs-4">
|
||||
<a href="${request.application_url}/demandes" tal:condition="access > 1">
|
||||
<div class="info-box bg-gest">
|
||||
<span class="info-box-icon"><i class="glyphicon glyphicon-envelope"></i></span>
|
||||
<div class="info-box-content">
|
||||
<span class="info-box-number">EMAILS</span>
|
||||
<span class="info-box-text">À IMPORTER DANS DOSSIERS</span>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-xs-4">
|
||||
<a href="${request.application_url}/upload_om" tal:condition="access > 1">
|
||||
<div class="info-box bg-gest">
|
||||
<span class="info-box-icon"><i class="glyphicon glyphicon-download-alt"></i></span>
|
||||
<div class="info-box-content">
|
||||
<span class="info-box-number">PDF <span class="glyphicon glyphicon-arrow-right"></span> DOSSIERS</span>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-xs-4">
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<div class="row">
|
||||
<div class="col-xs-4">
|
||||
<a href="${request.application_url}/dem_devis" tal:condition="access > 1">
|
||||
<div class="info-box bg-gest">
|
||||
<span class="info-box-icon"><i class="glyphicon glyphicon-folder-open"></i></span>
|
||||
<div class="info-box-content">
|
||||
<span class="info-box-number">DEM. DEVIS</span>
|
||||
<span class="info-box-text">À TRAITER </span>
|
||||
<span class="info-box-number" tal:condition="len(nb_dd_restants) > 0"><span class="badge bg-PE">${nb_dd_restants.nb_PE}</span>
|
||||
<span class="badge bg-ME">${nb_dd_restants.nb_ME}</span>
|
||||
<span class="badge bg-PL">${nb_dd_restants.nb_PL}</span></span>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-xs-4">
|
||||
<a href="${request.application_url}/dern_suivis" tal:condition="access > 1">
|
||||
<div class="info-box bg-gest">
|
||||
<span class="info-box-icon"><i class="glyphicon glyphicon-file"></i></span>
|
||||
<div class="info-box-content">
|
||||
<span class="info-box-number">CHANTIERS</span>
|
||||
<span class="info-box-text">DERNIERS SUIVIS</span>
|
||||
<span class="info-box-number" tal:condition="len(nb_de_restants) > 0"><span class="badge bg-PE">${nb_de_restants.nb_PE}</span>
|
||||
<span class="badge bg-ME">${nb_de_restants.nb_ME}</span>
|
||||
<span class="badge bg-PL">${nb_de_restants.nb_PL}</span></span>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-xs-4">
|
||||
<a href="${request.application_url}/factures_en_att" tal:condition="access > 1">
|
||||
<div class="info-box bg-gest">
|
||||
<span class="info-box-icon"><i class="glyphicon glyphicon-inbox"></i></span>
|
||||
<div class="info-box-content">
|
||||
<span class="info-box-number">FACTURES</span>
|
||||
<span class="info-box-text">À RÉGLER </span>
|
||||
<span class="info-box-number" tal:condition="len(nb_fa_restants) > 0"><span class="badge bg-PE">${nb_fa_restants.nb_PE}</span>
|
||||
<span class="badge bg-ME">${nb_fa_restants.nb_ME}</span>
|
||||
<span class="badge bg-PL">${nb_fa_restants.nb_PL}</span></span>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<div class="row">
|
||||
<div class="col-xs-4">
|
||||
<a href="${request.application_url}/stats" tal:condition="access > 6">
|
||||
<div class="info-box bg-compta">
|
||||
<span class="info-box-icon"><i class="glyphicon glyphicon-stats"></i></span>
|
||||
<div class="info-box-content">
|
||||
<span class="info-box-number">STATS</span>
|
||||
<span class="info-box-text">DÉLAIS ET CA</span>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-xs-4">
|
||||
<a href="${request.application_url}/devis_list/PE/0" tal:condition="logged_in == 'CAO'">
|
||||
<div class="info-box bg-prod">
|
||||
<span class="info-box-icon"><i class="glyphicon glyphicon-search"></i></span>
|
||||
<div class="info-box-content">
|
||||
<span class="info-box-number">RECHERCHE DEVIS</span>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
</div>
|
||||
|
||||
</div><!-- content -->
|
||||
</metal:block>
|
||||
|
||||
|
||||
@@ -1,155 +0,0 @@
|
||||
<metal:block use-macro="main_template">
|
||||
<div metal:fill-slot="content">
|
||||
|
||||
<div class="container-fluid">
|
||||
<br />
|
||||
<div class="row">
|
||||
<div class="col-sm-3">
|
||||
<a href="${request.application_url}/agenda/today">
|
||||
<div class="info-box bg-prod">
|
||||
<span class="info-box-icon"><i class="glyphicon glyphicon-calendar"></i></span>
|
||||
<div class="info-box-content">
|
||||
<span class="info-box-number">AGENDA</span>
|
||||
<span class="info-box-text">Rendez-vous : </span>
|
||||
<span class="info-box-number"><span class="badge bg-red">${nb_rdv.nb_rdv}</span></span>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
<a href="${request.application_url}/rdf_list">
|
||||
<div class="info-box bg-green">
|
||||
<span class="info-box-icon"><i class="glyphicon glyphicon-tint"></i></span>
|
||||
<div class="info-box-content">
|
||||
<span class="info-box-number">RDF</span>
|
||||
<span class="info-box-text">A traiter : </span>
|
||||
<span class="info-box-number"><span class="badge bg-red">${nb_rdf.nb_rdf}</span></span>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
<a href="${request.application_url}/devis_list">
|
||||
<div class="info-box bg-prod">
|
||||
<span class="info-box-icon"><i class="glyphicon glyphicon-text-height"></i></span>
|
||||
<div class="info-box-content">
|
||||
<span class="info-box-number">E-DEVIS</span>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
<a href="${request.application_url}/dossier_lookup" tal:condition="access > 6">
|
||||
<div class="info-box bg-prod">
|
||||
<span class="info-box-icon"><i class="glyphicon glyphicon-search"></i></span>
|
||||
<div class="info-box-content">
|
||||
<span class="info-box-number">RECHERCHE</span>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
|
||||
<div class="row" tal:condition="access >= 5">
|
||||
<div class="col-sm-3">
|
||||
<a href="${request.application_url}/planning/today">
|
||||
<div class="info-box bg-gest">
|
||||
<span class="info-box-icon"><i class="glyphicon glyphicon-calendar"></i></span>
|
||||
<div class="info-box-content">
|
||||
<span class="info-box-number">PLANNING</span>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
<a href="${request.application_url}/demandes" tal:condition="access > 0">
|
||||
<div class="info-box bg-gest">
|
||||
<span class="info-box-icon"><i class="glyphicon glyphicon-envelope"></i></span>
|
||||
<div class="info-box-content">
|
||||
<span class="info-box-number">EMAILS</span>
|
||||
<span class="info-box-text">A traiter : </span>
|
||||
<span class="info-box-number"><span class="badge bg-purple">${nb_mails}</span></span>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
<a href="${request.application_url}/upload_om" tal:condition="access > 0">
|
||||
<div class="info-box bg-gest">
|
||||
<span class="info-box-icon"><i class="glyphicon glyphicon-download-alt"></i></span>
|
||||
<div class="info-box-content">
|
||||
<span class="info-box-number">PDF <span class="glyphicon glyphicon-arrow-right"></span> DOSSIERS</span>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<div class="row">
|
||||
<div class="col-sm-3">
|
||||
<a href="${request.application_url}/dem_devis" tal:condition="access > 0">
|
||||
<div class="info-box bg-gest">
|
||||
<span class="info-box-icon"><i class="glyphicon glyphicon-folder-open"></i></span>
|
||||
<div class="info-box-content">
|
||||
<span class="info-box-number">DEM. DEVIS</span>
|
||||
<span class="info-box-text">A traiter : </span>
|
||||
<span class="info-box-number"><span class="badge bg-PE">${nb_dd_restants.nb_PE}</span>
|
||||
<span class="badge bg-ME">${nb_dd_restants.nb_ME}</span>
|
||||
<span class="badge bg-PL">${nb_dd_restants.nb_PL}</span></span>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
<a href="${request.application_url}/devis_en_att" tal:condition="access > 0">
|
||||
<div class="info-box bg-gest">
|
||||
<span class="info-box-icon"><i class="glyphicon glyphicon-file"></i></span>
|
||||
<div class="info-box-content">
|
||||
<span class="info-box-number">DEVIS</span>
|
||||
<span class="info-box-text">A traiter : </span>
|
||||
<span class="info-box-number"><span class="badge bg-PE">${nb_de_restants.nb_PE}</span>
|
||||
<span class="badge bg-ME">${nb_de_restants.nb_ME}</span>
|
||||
<span class="badge bg-PL">${nb_de_restants.nb_PL}</span></span>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
<a href="${request.application_url}/factures_en_att" tal:condition="access > 0">
|
||||
<div class="info-box bg-gest">
|
||||
<span class="info-box-icon"><i class="glyphicon glyphicon-inbox"></i></span>
|
||||
<div class="info-box-content">
|
||||
<span class="info-box-number">FACTURES</span>
|
||||
<span class="info-box-text">A traiter : </span>
|
||||
<span class="info-box-number"><span class="badge bg-PE">${nb_fa_restants.nb_PE}</span>
|
||||
<span class="badge bg-ME">${nb_fa_restants.nb_ME}</span>
|
||||
<span class="badge bg-PL">${nb_fa_restants.nb_PL}</span></span>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<div class="row">
|
||||
<div class="col-sm-3">
|
||||
<a href="${request.application_url}/stats" tal:condition="access > 6">
|
||||
<div class="info-box bg-compta">
|
||||
<span class="info-box-icon"><i class="glyphicon glyphicon-stats"></i></span>
|
||||
<div class="info-box-content">
|
||||
<span class="info-box-number">STATS</span>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
</div>
|
||||
|
||||
</div><!-- content -->
|
||||
</metal:block>
|
||||
|
||||
@@ -14,12 +14,9 @@
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2">Type de texte</label>
|
||||
<div class="col-sm-10">
|
||||
<label class="radio-inline"><input type="radio" name="ref" value="T1"
|
||||
tal:attributes="checked ligne.ref=='T1'">Titre</label>
|
||||
<label class="radio-inline"><input type="radio" name="ref" value="T2"
|
||||
tal:attributes="checked ligne.ref=='T2'">Sous-titre</label>
|
||||
<label class="radio-inline"><input type="radio" name="ref" value="TX"
|
||||
tal:attributes="checked ligne.ref=='TX'">Texte libre</label>
|
||||
<label class="radio-inline"><input type="radio" name="ref" value="T1" checked>Titre</label>
|
||||
<label class="radio-inline"><input type="radio" name="ref" value="T2">Sous-titre</label>
|
||||
<label class="radio-inline"><input type="radio" name="ref" value="TX">Texte libre</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
@@ -47,8 +44,11 @@
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2">Quantité</label>
|
||||
<div class="col-sm-10">
|
||||
<input class="form-control" type="text" id="qte" name="qte" value="${ligne.qte}">
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon" id="unite">.00</span>
|
||||
<input class="form-control" type="text" id="qte" name="qte" value="${ligne.qte}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2">Prix HT</label>
|
||||
@@ -196,11 +196,13 @@ $(document).ready(function() {
|
||||
var ref = response[0]['ref'];
|
||||
var libelle = response[0]['libelle'];
|
||||
var prixht = response[0]['prixht'];
|
||||
var unite = response[0]['unite'];
|
||||
|
||||
// Set value to textboxes
|
||||
document.getElementById('ref').value = ref;
|
||||
document.getElementById('libelle').value = libelle;
|
||||
document.getElementById('prixht').value = prixht;
|
||||
document.getElementById("unite").innerHTML= unite;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -27,10 +27,9 @@
|
||||
</div>
|
||||
</div>
|
||||
<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">
|
||||
<input type="text" class="form-control" name="name" value="${name}"
|
||||
placeholder="Le nom ou le numéro doit avoir de 2 à 30 caractères de long" >
|
||||
<input type="text" class="form-control" name="nodevis" value="${nodevis}" >
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -53,19 +52,20 @@
|
||||
<th>Client</th>
|
||||
<th>Chantier</th>
|
||||
<th class="text-right">Montant</th>
|
||||
<th>Sinistre</th>
|
||||
<th class="text-center">Statut</th>
|
||||
<th>No chantier</th>
|
||||
<th class="text-center">Action</th>
|
||||
</tr>
|
||||
<tr tal:repeat="detail devis">
|
||||
<td>
|
||||
<a href="/devis_web/${societe}-DE${detail.numero}">${societe}-${detail.numero}-W</a>
|
||||
</td>
|
||||
<tr tal:repeat="detail dossiers">
|
||||
<td>${detail.TYPE}-${detail.numero}</td>
|
||||
<td>${detail.date.strftime('%d-%m-%Y')}</td>
|
||||
<td>${detail.nomcli}</td>
|
||||
<td>${detail.chantier}</td>
|
||||
<td class="text-right">${layout.to_euro(detail.montant)}</td>
|
||||
<td>${detail.nosin}</td>
|
||||
<td class="text-center">${detail.status}</td>
|
||||
<td>${detail.nochantier}</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>
|
||||
|
||||
</thead>
|
||||
@@ -74,26 +74,53 @@
|
||||
<br />
|
||||
</div>
|
||||
|
||||
<!-- Modal : Confirmation CREATION -->
|
||||
<div class="modal fade" id="confirmCreate" role="dialog" aria-labelledby="confirmCreateLabel" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h4 class="modal-title">Ajouter le nochantier dans le </h4>
|
||||
</div>
|
||||
<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">
|
||||
<div class="form-group">
|
||||
<label class="control-label col-xs-4">Document No:</label>
|
||||
<div class="col-xs-8">
|
||||
<input type="text" name="md_nodevis" id="md_nodevis" value="" readonly/>
|
||||
</div>
|
||||
</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">
|
||||
$(document).ready(function() {
|
||||
$('#devis-search-form').formValidation({
|
||||
framework: 'bootstrap',
|
||||
message: 'This value is not valid',
|
||||
icon: {
|
||||
valid: 'glyphicon glyphicon-ok',
|
||||
invalid: 'glyphicon glyphicon-remove',
|
||||
validating: 'glyphicon glyphicon-refresh'
|
||||
},
|
||||
});
|
||||
$('form input').on('keypress', function(e) {
|
||||
var code = e.keyCode || e.which;
|
||||
if (code === 13) {
|
||||
e.preventDefault();
|
||||
// simuler clic bouton submit
|
||||
document.getElementById("submitButton").click();
|
||||
}
|
||||
});
|
||||
|
||||
$('#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>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
<td style="width:18%; text-align:right">Montant HT</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="4"><hr></td>
|
||||
<td colspan="5"><hr></td>
|
||||
</tr>
|
||||
<!-- clignes de détail du devis -->
|
||||
<div tal:replace="structure dt_html">Les lignes du devis ici</div>
|
||||
|
||||
@@ -59,19 +59,18 @@
|
||||
</table>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<h4>Statut : ${entete.libelle}</h4>
|
||||
<h4>
|
||||
Statut : ${entete.libelle}
|
||||
[ <a href="#" data-toggle="modal" data-target="#confirmStatut" tal:condition="access > 1">Modifier le statut</a> ]
|
||||
</h4>
|
||||
<div tal:condition="type_doc=='DE'">
|
||||
<p>Dernière modif. le <b>${entete.DATEMAJ.strftime('%d/%m/%Y à %H:%M')}</b> par <b>${entete.USERMAJ}</b></p>
|
||||
<p>
|
||||
<a class="btn btn-warning" role="button" href="#"
|
||||
data-toggle="modal" data-target="#confirmCloture"><span class="glyphicon glyphicon-check"></span> Modif. statut</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div> <!-- row -->
|
||||
<!-- ENTETE entete -->
|
||||
<table class="table table-bordered table-condensed">
|
||||
<table class="table table-bordered table-condensed" tal:condition="access > 1">
|
||||
<tr class="well">
|
||||
<th class="text-right">Total HT</th>
|
||||
<th class="text-right">Total TVA</th>
|
||||
@@ -98,28 +97,29 @@
|
||||
<tr tal:repeat="detail details">
|
||||
<td>${detail.REF}</td>
|
||||
<td>${detail.LIB}</td>
|
||||
<td class="text-right">${layout.to_euro(detail.QTE)}</td>
|
||||
<td class="text-right">${layout.to_euroz(detail.PRIXHT)}</td>
|
||||
<td class="text-right">${layout.to_euroz(detail.MTHT)}</td>
|
||||
<td class="text-right">${detail.QTE}</td>
|
||||
<td class="text-right"><span tal:condition="access > 1">${layout.to_euroz(detail.PRIXHT)}</span></td>
|
||||
<td class="text-right"><span tal:condition="access > 1">${layout.to_euroz(detail.MTHT)}</span></td>
|
||||
<td class="text-center">${detail.USERMAJ}</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<!-- Modal : Confirmation CLOTURE -->
|
||||
<div class="modal fade" id="confirmCloture" role="dialog" aria-labelledby="confirmClotureLabel" aria-hidden="true">
|
||||
<div class="modal fade" id="confirmStatut" role="dialog" aria-labelledby="confirmStatutLabel" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h4 class="modal-title">Clôturer le devis</h4>
|
||||
<h4 class="modal-title">Modifier le statut du devis ou facture</h4>
|
||||
</div>
|
||||
<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">
|
||||
<div class="form-group">
|
||||
<p class="text-center"><b>Voulez-vous changer le status du devis ?</b></p>
|
||||
<label class="control-label col-xs-4" for="status">Devis / Facture</label>
|
||||
<p class="form-control-static col-xs-8"><b>${entete.societe}-${entete.NO_ID} - ${entete.C_QUALITE} ${entete.C_NOM}</b></p>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="form-group">
|
||||
<label class="control-label col-xs-4" for="status">Sélectionner le statut :</label>
|
||||
<div class="col-xs-8">
|
||||
<select class="form-control" id="status" name="status">
|
||||
@@ -129,17 +129,15 @@
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
<div class="row">
|
||||
<div class="form-group">
|
||||
<label class="control-label col-xs-4" for="motif">Motif :</label>
|
||||
<div class="col-xs-8">
|
||||
<input class="form-control" type="text" id="motif" name="motif" value=""
|
||||
placeholder="65 caractères maximum"
|
||||
data-fv-notempty="true"
|
||||
data-fv-notempty-message="Veuillez remplir un motif"
|
||||
data-fv-stringlength="true"
|
||||
data-fv-stringlength-max="65"
|
||||
data-fv-stringlength-message="65 caractères maximum"/>
|
||||
<select class="form-control" id="motif" name="motif">
|
||||
<option selected> </option>
|
||||
<div tal:repeat="item motifs">
|
||||
<option>${item.code} | ${item.libelle}</option>
|
||||
</div>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
|
||||
108
mondumas/templates/devis/factures_en_att.pt
Normal file
108
mondumas/templates/devis/factures_en_att.pt
Normal file
@@ -0,0 +1,108 @@
|
||||
<metal:block use-macro="main_template">
|
||||
<div metal:fill-slot="content">
|
||||
|
||||
<div class="row">
|
||||
|
||||
<form id="site-search-form" class="form-horizontal" role="form" action="${url}" method="post"
|
||||
data-fv-framework="bootstrap"
|
||||
data-fv-icon-valid="glyphicon glyphicon-ok"
|
||||
data-fv-icon-invalid="glyphicon glyphicon-remove"
|
||||
data-fv-icon-validating="glyphicon glyphicon-refresh">
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-4 control-label">Société</label>
|
||||
<div class="col-xs-8">
|
||||
<label class="radio-inline"><input type="radio" name="societe" value="PE"
|
||||
tal:attributes="checked societe=='PE'">PE</label>
|
||||
<label class="radio-inline"><input type="radio" name="societe" value="ME"
|
||||
tal:attributes="checked societe=='ME'">ME</label>
|
||||
<label class="radio-inline"><input type="radio" name="societe" value="PL"
|
||||
tal:attributes="checked societe=='PL'">PL</label>
|
||||
<label class="radio-inline"><input type="radio" name="societe" value="PO"
|
||||
tal:attributes="checked societe=='PO'">PO</label>
|
||||
<label class="radio-inline"><input type="radio" name="societe" value="CD"
|
||||
tal:attributes="checked societe=='CD'">CD</label>
|
||||
<button id="submitButton" class="btn btn-primary" type="submit" name="form.submitted">
|
||||
<i class="glyphicon glyphicon-search"></i> Rechercher</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div><!-- row -->
|
||||
|
||||
<br />
|
||||
|
||||
<div class="row">
|
||||
<table id="dossiers_list" class="table table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Numéro</th>
|
||||
<th>Date</th>
|
||||
<th>Client</th>
|
||||
<th>Chantier</th>
|
||||
<th class="text-right">Montant</th>
|
||||
<th>Sinistre</th>
|
||||
<th class="text-center">Statut</th>
|
||||
<th>Uti.</th>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
</div>
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<script type="text/javascript">
|
||||
var dataSet = ${dt_data};
|
||||
var goto_url = '${goto_url}';
|
||||
var order_option = '${order_option}'
|
||||
|
||||
$(document).ready(function() {
|
||||
$.fn.dataTable.moment('DD-MM-YYYY');
|
||||
$('#dossiers_list').DataTable({
|
||||
data: dataSet,
|
||||
pageLength: 100,
|
||||
bLengthChange: false,
|
||||
language: {
|
||||
url: 'https://cdn.datatables.net/plug-ins/1.10.16/i18n/French.json'
|
||||
},
|
||||
order: [[0, order_option]],
|
||||
columnDefs: [
|
||||
{ className: "text-right", "targets": [4] },
|
||||
{ "targets": 0,
|
||||
"render": function (data, type, full, meta) {
|
||||
// ajouter un link vers le formulaire
|
||||
return '<a href="' + goto_url + data + '">' + data + '</a>';
|
||||
},
|
||||
},
|
||||
],
|
||||
createdRow: function( row, data, dataIndex ){
|
||||
if ( data[6] == "Humidité" ) {
|
||||
$('td', row).eq(6).css('background-color', 'Crimson').css('color', 'white');
|
||||
}
|
||||
if ( data[6] == "Devis" || data[6] == "Commandé") {
|
||||
$('td', row).eq(6).css('background-color', 'Orange');
|
||||
}
|
||||
if ( data[6] == "Facturé") {
|
||||
$('td', row).eq(6).css('background-color', 'LightBlue');
|
||||
}
|
||||
if ( data[6] == "Réglée" || data[6] == "Régl part.") {
|
||||
$('td', row).eq(6).css('background-color', 'LightGreen');
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
});
|
||||
$('form input').on('keypress', function(e) {
|
||||
var code = e.keyCode || e.which;
|
||||
if (code === 13) {
|
||||
e.preventDefault();
|
||||
// simuler clic bouton submit
|
||||
document.getElementById("submitButton").click();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
</div><!-- content -->
|
||||
</metal:block>
|
||||
|
||||
|
||||
@@ -1,27 +1,105 @@
|
||||
<metal:block use-macro="main_template">
|
||||
<div metal:fill-slot="content">
|
||||
|
||||
<div class="row">
|
||||
|
||||
<form id="site-search-form" class="form-horizontal" role="form" action="${url}" method="post"
|
||||
data-fv-framework="bootstrap"
|
||||
data-fv-icon-valid="glyphicon glyphicon-ok"
|
||||
data-fv-icon-invalid="glyphicon glyphicon-remove"
|
||||
data-fv-icon-validating="glyphicon glyphicon-refresh">
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-4 control-label">Société</label>
|
||||
<div class="col-xs-8">
|
||||
<label class="radio-inline"><input type="radio" name="societe" value="PE"
|
||||
tal:attributes="checked societe=='PE'">PE</label>
|
||||
<label class="radio-inline"><input type="radio" name="societe" value="ME"
|
||||
tal:attributes="checked societe=='ME'">ME</label>
|
||||
<label class="radio-inline"><input type="radio" name="societe" value="PL"
|
||||
tal:attributes="checked societe=='PL'">PL</label>
|
||||
<label class="radio-inline"><input type="radio" name="societe" value="PO"
|
||||
tal:attributes="checked societe=='PO'">PO</label>
|
||||
<label class="radio-inline"><input type="radio" name="societe" value="CD"
|
||||
tal:attributes="checked societe=='CD'">CD</label>
|
||||
<button id="submitButton" class="btn btn-primary" type="submit" name="form.submitted">
|
||||
<i class="glyphicon glyphicon-search"></i> Rechercher</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div><!-- row -->
|
||||
|
||||
<br />
|
||||
|
||||
<table class="table table-condensed">
|
||||
<tr tal:repeat="item dossiers_traites">
|
||||
<td>${item.DATEMAJ.strftime('%d %b')}</td>
|
||||
<td><a href="${request.application_url}/dossier_view/${item.societe}-${item.NO_ID}">${item.societe}-${item.NO_ID}</td>
|
||||
<td>${item.NOMCLI}</td>
|
||||
<td>${item.C_NOM}</td>
|
||||
<td>${item.USERMAJ}</td>
|
||||
<td><span class="badge bg-${item.STATUS}">${item.libelle}</span></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<div class="row">
|
||||
<table id="dossiers_list" class="table table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Numéro</th>
|
||||
<th>Date</th>
|
||||
<th>Client</th>
|
||||
<th>Chantier</th>
|
||||
<th class="text-right">Montant</th>
|
||||
<th>Sinistre</th>
|
||||
<th class="text-center">Statut</th>
|
||||
<th>Uti.</th>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
</div>
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<script type="text/javascript">
|
||||
$('#generateButton').on('click', function(){
|
||||
$('i.gly-spin').removeClass('gly-spin');
|
||||
$('i').addClass('gly-spin');
|
||||
});
|
||||
var dataSet = ${dt_data};
|
||||
var goto_url = '${goto_url}';
|
||||
var order_option = '${order_option}'
|
||||
|
||||
$(document).ready(function() {
|
||||
$.fn.dataTable.moment('DD-MM-YYYY');
|
||||
$('#dossiers_list').DataTable({
|
||||
data: dataSet,
|
||||
pageLength: 100,
|
||||
bLengthChange: false,
|
||||
language: {
|
||||
url: 'https://cdn.datatables.net/plug-ins/1.10.16/i18n/French.json'
|
||||
},
|
||||
order: [[0, order_option]],
|
||||
columnDefs: [
|
||||
{ className: "text-right", "targets": [4] },
|
||||
{ "targets": 0,
|
||||
"render": function (data, type, full, meta) {
|
||||
// ajouter un link vers le formulaire
|
||||
return '<a href="' + goto_url + data + '">' + data + '</a>';
|
||||
},
|
||||
},
|
||||
],
|
||||
createdRow: function( row, data, dataIndex ){
|
||||
if ( data[6] == "Humidité" ) {
|
||||
$('td', row).eq(6).css('background-color', 'Crimson').css('color', 'white');
|
||||
}
|
||||
if ( data[6] == "Devis" || data[6] == "Commandé") {
|
||||
$('td', row).eq(6).css('background-color', 'Orange');
|
||||
}
|
||||
if ( data[6] == "Facturé") {
|
||||
$('td', row).eq(6).css('background-color', 'LightBlue');
|
||||
}
|
||||
if ( data[6] == "Réglée" || data[6] == "Régl part.") {
|
||||
$('td', row).eq(6).css('background-color', 'LightGreen');
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
});
|
||||
$('form input').on('keypress', function(e) {
|
||||
var code = e.keyCode || e.which;
|
||||
if (code === 13) {
|
||||
e.preventDefault();
|
||||
// simuler clic bouton submit
|
||||
document.getElementById("submitButton").click();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
</div><!-- content -->
|
||||
|
||||
@@ -29,6 +29,18 @@
|
||||
class="btn btn-primary btn-sm"><i class="glyphicon glyphicon-arrow-down"></i></a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<br />
|
||||
<h2>Dossiers générés à contrôler</h2>
|
||||
<table class="table table-condensed">
|
||||
<tr tal:repeat="item dossiers_traites">
|
||||
<td>${item.DATEMAJ.strftime('%d %b')}</td>
|
||||
<td><a href="${request.application_url}/dossier_view/${item.societe}-${item.NO_ID}">${item.societe}-${item.NO_ID}</td>
|
||||
<td>${item.NOMCLI}</td>
|
||||
<td>${item.C_NOM}</td>
|
||||
<td>${item.USERMAJ}</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<br />
|
||||
|
||||
31
mondumas/templates/dossier/dern_suivis.pt
Normal file
31
mondumas/templates/dossier/dern_suivis.pt
Normal file
@@ -0,0 +1,31 @@
|
||||
<metal:block use-macro="main_template">
|
||||
<div metal:fill-slot="content">
|
||||
|
||||
<br />
|
||||
|
||||
<div class="row">
|
||||
<table class="table table-bordered table-condensed">
|
||||
<tr>
|
||||
<th>Date</th>
|
||||
<th>Dossier</th>
|
||||
<th>Action - commentaire</th>
|
||||
<th class="text-center">Par</th>
|
||||
</tr>
|
||||
<tr tal:repeat="item items">
|
||||
<td>${item.DATE.strftime('%d-%m-%Y')}</td>
|
||||
<td>
|
||||
<a href="${request.route_url('dossier_view', nodossier=item.nodossier)}">
|
||||
${item.nodossier} - ${item.C_NOM}</a>
|
||||
</td>
|
||||
<td>${item.COMMENT}</td>
|
||||
<td class="text-center">${item.USERMAJ}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<br />
|
||||
<br />
|
||||
|
||||
</div><!-- content -->
|
||||
</metal:block>
|
||||
|
||||
|
||||
60
mondumas/templates/dossier/dessin_edit.pt
Normal file
60
mondumas/templates/dossier/dessin_edit.pt
Normal file
@@ -0,0 +1,60 @@
|
||||
<metal:block use-macro="main_template">
|
||||
<div metal:fill-slot="content">
|
||||
|
||||
<div class="alert alert-danger" tal:condition="message" tal:content="message" />
|
||||
|
||||
<div class="row">
|
||||
<form id="dessin_edit-form" action="${url}" method="post">
|
||||
<div class="form-group">
|
||||
<a href="${request.application_url}/dossier_view/${nodossier}#tab_attaches" class="btn btn-default" role="button">
|
||||
<span class="glyphicon glyphicon-chevron-left"></span> Annuler</a>
|
||||
<button class="btn btn-primary" type="submit" name="form.submitted">
|
||||
<span class="glyphicon glyphicon-ok"></span> Enregistrer</button>
|
||||
</div>
|
||||
<!-- this will be the input used to pass the drawingboard content to the server -->
|
||||
<input type="hidden" name="image" value="">
|
||||
|
||||
</form>
|
||||
</div> <!-- row -->
|
||||
<div style="row">
|
||||
<div id="dessin"></div>
|
||||
</div> <!-- row -->
|
||||
|
||||
<!-- Drawingboard -->
|
||||
<link href="${request.static_url('mondumas:static/dist/drawingboard/drawingboard.min.css')}" rel="stylesheet" media="all">
|
||||
<script src="${request.static_url('mondumas:static/dist/drawingboard/drawingboard.min.js')}"></script>
|
||||
<script>
|
||||
var simpleBoard = new DrawingBoard.Board('dessin', {
|
||||
background: 'none',
|
||||
size: 10,
|
||||
controls: [
|
||||
'Color',
|
||||
{ Size: { type: 'dropdown' } },
|
||||
{ Navigation: { } },
|
||||
],
|
||||
size: 1,
|
||||
webStorage: 'session',
|
||||
enlargeYourContainer: true
|
||||
});
|
||||
$(document).ready(function() {
|
||||
$('#dessin_edit-form').on('submit', function(e) {
|
||||
//get drawingboard content
|
||||
var img = simpleBoard.getImg();
|
||||
|
||||
//we keep drawingboard content only if it's not the 'blank canvas'
|
||||
var imgInput = (simpleBoard.blankCanvas == img) ? '' : img;
|
||||
// alert("imgInput : " + imgInput);
|
||||
|
||||
//put the drawingboard content in the form field to send it to the server
|
||||
$(this).find('input[name=image]').val( imgInput );
|
||||
|
||||
//we can also assume that everything goes well server-side
|
||||
//and directly clear webstorage here so that the drawing isn't shown again after form submission
|
||||
//but the best would be to do when the server answers that everything went well
|
||||
simpleBoard.clearWebStorage();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
</div><!-- content -->
|
||||
</metal:block>
|
||||
@@ -1,30 +0,0 @@
|
||||
<metal:block use-macro="main_template">
|
||||
<div metal:fill-slot="content">
|
||||
|
||||
<br />
|
||||
|
||||
<table class="table table-condensed">
|
||||
<tr tal:repeat="item list_devis_en_att">
|
||||
<td>${item.DATEMAJ.strftime('%d %b')}</td>
|
||||
<td><a href="${request.application_url}/devis_view/${item.societe}-DE${item.NO_ID}">${item.societe}-DE${item.NO_ID}</td>
|
||||
<td>${item.NOMCLI}</td>
|
||||
<td>${item.C_NOM}</td>
|
||||
<td>${item.USERMAJ}</td>
|
||||
<td><span class="badge bg-${item.STATUS}">${item.libelle}</span></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<script type="text/javascript">
|
||||
$('#generateButton').on('click', function(){
|
||||
$('i.gly-spin').removeClass('gly-spin');
|
||||
$('i').addClass('gly-spin');
|
||||
});
|
||||
</script>
|
||||
|
||||
</div><!-- content -->
|
||||
</metal:block>
|
||||
|
||||
|
||||
@@ -65,6 +65,16 @@
|
||||
data-fv-stringlength-message="20 caractères maximum" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-xs-3 control-label">Observation</label>
|
||||
<div class="col-xs-5">
|
||||
<input class="form-control" type="text" name="C_OBS"
|
||||
value="${dossier.C_OBS}" placeholder="40 caractères maximum"
|
||||
data-fv-stringlength="true"
|
||||
data-fv-stringlength-max="40"
|
||||
data-fv-stringlength-message="40 caractères maximum" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3 class="text-primary">EMAIL et TELEPHONES</h3>
|
||||
<div class="form-group">
|
||||
|
||||
@@ -9,7 +9,8 @@
|
||||
<tr>
|
||||
<td>
|
||||
<h4>CHANTIER</h4>
|
||||
<a href="${request.application_url}/dossier_edit/${nodossier}" class="btn btn-primary" role="button">
|
||||
<a href="${request.application_url}/dossier_edit/${nodossier}" tal:condition="access > 1"
|
||||
class="btn btn-primary" role="button">
|
||||
<span class="glyphicon glyphicon-pencil"></span> Modifier</a>
|
||||
</td>
|
||||
<td>
|
||||
@@ -26,15 +27,17 @@
|
||||
<td>
|
||||
Etage - Code<br />
|
||||
Ascenseur<br />
|
||||
Observation<br />
|
||||
Téléphone 1 et 2<br />
|
||||
Portable 1 et 2
|
||||
Portable 1 et 2<br />
|
||||
</td>
|
||||
<td>
|
||||
${dossier.C_ETAGE} - ${dossier.C_CODE}<br />
|
||||
<span tal:condition="dossier.c_ascenseur==0">NON<br /></span>
|
||||
<span tal:condition="dossier.c_ascenseur!=0">OUI<br /></span>
|
||||
${dossier.C_OBS}<br />
|
||||
${dossier.C_TEL1} - ${dossier.C_TEL2}<br />
|
||||
${dossier.C_TELP} - ${dossier.C_FAX}
|
||||
${dossier.C_TELP} - ${dossier.C_FAX} <br />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -54,14 +57,12 @@
|
||||
Police<br />
|
||||
Sinistre<br />
|
||||
Votre référence<br />
|
||||
Observation<br />
|
||||
Travaux<br />
|
||||
</td>
|
||||
<td>
|
||||
${dossier.NOPOL}<br />
|
||||
${dossier.NOSIN}<br />
|
||||
${dossier.VREF}<br />
|
||||
${dossier.C_OBS}<br />
|
||||
${dossier.TX_TRAV}<br />
|
||||
</td>
|
||||
</tr>
|
||||
@@ -97,12 +98,12 @@
|
||||
</table>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<h4>Statut : ${dossier.libelle}</h4>
|
||||
<br />
|
||||
<h4>
|
||||
Statut : <span class="label label-warning">${dossier.libelle}</span>
|
||||
[ <a href="#" data-toggle="modal" data-target="#confirmStatut" tal:condition="access > 1">Modifier le statut</a> ]
|
||||
</h4>
|
||||
<p>Dernière modif. le <b>${dossier.DATEMAJ.strftime('%d/%m/%Y à %H:%M')}</b> par <b>${dossier.USERMAJ}</b></p>
|
||||
<p>
|
||||
<a class="btn btn-warning" role="button" href="#"
|
||||
data-toggle="modal" data-target="#confirmCloture"><span class="glyphicon glyphicon-check"></span> Modif. statut dossier</a>
|
||||
</p>
|
||||
|
||||
</div>
|
||||
</div> <!-- row -->
|
||||
@@ -129,10 +130,8 @@
|
||||
|
||||
<ul class="nav nav-tabs">
|
||||
<li class="active"><a data-toggle="tab" href="#tab_suivi"><b>SUIVI du DOSSIER</b></a></li>
|
||||
<li tal:condition="access > 0">
|
||||
<a data-toggle="tab" href="#tab_documents"><b>DEVIS - FACTURES</b></a></li>
|
||||
<li tal:condition="access > 0">
|
||||
<a data-toggle="tab" href="#tab_attaches"><b>DOCUMENTS ATTACHES</b></a></li>
|
||||
<li><a data-toggle="tab" href="#tab_documents"><b>DEVIS - FACTURES</b></a></li>
|
||||
<li><a data-toggle="tab" href="#tab_attaches"><b>DOCUMENTS ATTACHES</b></a></li>
|
||||
<li tal:condition="nodossier.startswith('PL')">
|
||||
<a data-toggle="tab" href="#tab_rdf"><b>RAPPORTS DE RDF</b></a></li>
|
||||
</ul>
|
||||
@@ -140,7 +139,7 @@
|
||||
<div class="tab-content">
|
||||
<div id="tab_suivi" class="tab-pane fade in active">
|
||||
<h3>SUIVI du DOSSIER</h3>
|
||||
<p>
|
||||
<p tal:condition="access > 1">
|
||||
<a class="btn btn-success" role="button" href="${request.route_url('suivi_edit', nodossier=nodossier, nolig='0')}">
|
||||
<span class="glyphicon glyphicon-plus"></span> Nouvelle ligne</a>
|
||||
<a class="btn btn-success" role="button" href="${request.route_url('rdv_edit', nodossier=nodossier, nolig='0')}">
|
||||
@@ -209,8 +208,8 @@
|
||||
</td>
|
||||
<td>${detail.date.strftime('%d-%m-%Y')}</td>
|
||||
<td>${detail.nomcli}</td>
|
||||
<td class="text-right">${layout.to_euro(detail.montant)}</td>
|
||||
<td class="text-center">${detail.status}</td>
|
||||
<td class="text-right"><span tal:condition="access > 1">${layout.to_euro(detail.montant)}</span></td>
|
||||
<td class="text-center">${detail.lib_status}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
@@ -249,7 +248,6 @@
|
||||
<td class="text-center">${detail.usermaj}</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<h3 class="text-center">DOCUMENTS TECHNIQUES</h3>
|
||||
<p>
|
||||
<a href="${request.application_url}/upload_doc/${nodossier}/FRN" class="btn btn-success" role="button">
|
||||
@@ -282,6 +280,25 @@
|
||||
<td class="text-center">${detail.usermaj}</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<h3 class="text-center">NOTES et DESSIN</h3>
|
||||
<p>
|
||||
<a href="${request.application_url}/note_edit/${nodossier}/0" class="btn btn-success" role="button">
|
||||
<span class="glyphicon glyphicon-text-size"></span> Ajouter une NOTE</a>
|
||||
<a href="${request.application_url}/dessin_edit/${nodossier}/0" class="btn btn-success" role="button">
|
||||
<span class="glyphicon glyphicon-picture"></span> Ajouter dessin</a>
|
||||
</p>
|
||||
<br />
|
||||
<div class="row">
|
||||
<!-- Listes des Notes -->
|
||||
<div class="text-center" tal:repeat="item dem_notes">
|
||||
<div class="col-sm-3">
|
||||
<a href="${request.application_url}/note_edit/${nodossier}/${item.noligne}">
|
||||
<span class="glyphicon glyphicon-text-size logo-primary"></span>
|
||||
<h4>${item.libelle}</h4></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- PANEL RDF -->
|
||||
<div id="tab_rdf" class="tab-pane fade">
|
||||
@@ -315,21 +332,22 @@
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Modal : Confirmation CLOTURE -->
|
||||
<div class="modal fade" id="confirmCloture" role="dialog" aria-labelledby="confirmClotureLabel" aria-hidden="true">
|
||||
<!-- Modal : Confirmation MODIF STATUT -->
|
||||
<div class="modal fade" id="confirmStatut" role="dialog" aria-labelledby="confirmStatutLabel" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h4 class="modal-title">Clôturer le dossier</h4>
|
||||
<h4 class="modal-title">Modifier le statut du dossier</h4>
|
||||
</div>
|
||||
<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">
|
||||
<div class="form-group">
|
||||
<p class="text-center"><b>Voulez-vous clôturer le dossier ?</b></p>
|
||||
<label class="control-label col-xs-4" for="status">Dossier</label>
|
||||
<p class="form-control-static col-xs-8"><b>${nodossier} ${dossier.C_QUALITE} ${dossier.C_NOM}</b></p>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="form-group">
|
||||
<label class="control-label col-xs-4" for="status">Sélectionner le statut :</label>
|
||||
<div class="col-xs-8">
|
||||
<select class="form-control" id="status" name="status">
|
||||
@@ -339,29 +357,43 @@
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
<div class="row">
|
||||
<div class="form-group">
|
||||
<label class="control-label col-xs-4" for="motif">Motif :</label>
|
||||
<div class="col-xs-8">
|
||||
<input class="form-control" type="text" id="motif" name="motif" value=""
|
||||
placeholder="65 caractères maximum"
|
||||
data-fv-notempty="true"
|
||||
data-fv-notempty-message="Veuillez remplir un motif"
|
||||
data-fv-stringlength="true"
|
||||
data-fv-stringlength-max="65"
|
||||
data-fv-stringlength-message="65 caractères maximum"/>
|
||||
<select class="form-control" id="motif" name="motif">
|
||||
<option selected> </option>
|
||||
<div tal:repeat="item motifs">
|
||||
<option>${item.libelle}</option>
|
||||
</div>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Annuler</button>
|
||||
<button type="submit" class="btn btn-warning" name="form.close">Clôturer</button>
|
||||
<button type="submit" class="btn btn-warning" name="form.close">Modifier</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
$(function() {
|
||||
// Javascript to enable link to tab
|
||||
var hash = document.location.hash;
|
||||
if (hash) {
|
||||
console.log(hash);
|
||||
$('.nav-tabs a[href="'+hash+'"]').tab('show');
|
||||
}
|
||||
|
||||
// Change hash for page-reload
|
||||
$('a[data-toggle="tab"]').on('show.bs.tab', function (e) {
|
||||
window.location.hash = e.target.hash;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
</div>
|
||||
</metal:block>
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
<metal:block use-macro="main_template">
|
||||
<div metal:fill-slot="content">
|
||||
|
||||
<br />
|
||||
|
||||
<table class="table table-condensed">
|
||||
<tr tal:repeat="item list_factures_en_att">
|
||||
<td>${item.DATEMAJ.strftime('%d %b')}</td>
|
||||
<td><a href="${request.application_url}/devis_view/${item.societe}-FA${item.NO_ID}">${item.societe}-FA${item.NO_ID}</td>
|
||||
<td>${item.NOMCLI}</td>
|
||||
<td>${item.C_NOM}</td>
|
||||
<td>${item.USERMAJ}</td>
|
||||
<td><span class="badge bg-${item.STATUS}">${item.libelle}</span></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<script type="text/javascript">
|
||||
$('#generateButton').on('click', function(){
|
||||
$('i.gly-spin').removeClass('gly-spin');
|
||||
$('i').addClass('gly-spin');
|
||||
});
|
||||
</script>
|
||||
|
||||
</div><!-- content -->
|
||||
</metal:block>
|
||||
|
||||
|
||||
37
mondumas/templates/dossier/note_edit.pt
Normal file
37
mondumas/templates/dossier/note_edit.pt
Normal file
@@ -0,0 +1,37 @@
|
||||
<metal:block use-macro="main_template">
|
||||
<div metal:fill-slot="content">
|
||||
|
||||
<div class="alert alert-danger" tal:condition="message" tal:content="message" />
|
||||
|
||||
<div class="row">
|
||||
<form id="text_edit-form" action="${url}" method="post">
|
||||
<div class="form-group">
|
||||
<a href="${request.application_url}/dossier_view/${nodossier}#tab_attaches" class="btn btn-default" role="button">
|
||||
<span class="glyphicon glyphicon-chevron-left"></span> Annuler</a>
|
||||
<button class="btn btn-primary" type="submit" name="form.submitted">
|
||||
<span class="glyphicon glyphicon-ok"></span> Enregistrer</button>
|
||||
<button class="btn btn-warning" type="submit" name="form.deleted">
|
||||
<span class="glyphicon glyphicon-remove"></span> Supprimer</button>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label" for="text-text">Tapez ou dictez le texte :</label>
|
||||
<textarea class="form-control monospace-font" rows="30" cols="40" id="notes" name="notes">${note.notes}</textarea>
|
||||
</div>
|
||||
<p>Modifié le : ${note.modif_le.strftime('%d-%m-%Y')} par ${note.usermaj}
|
||||
</p>
|
||||
</form>
|
||||
</div> <!-- row -->
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
$('#text_edit-form').formValidation();
|
||||
$('form input').on('keypress', function(e) {
|
||||
return e.which !== 13;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
</div>
|
||||
</metal:block>
|
||||
@@ -211,7 +211,7 @@
|
||||
<label class="checkbox-inline"><input type="checkbox" name="visu_endoscope" value="${rapport.visu_endoscope}"
|
||||
tal:attributes="checked rapport.visu_endoscope != 0 and 'checked' or None">Endoscope à fibre optique</label>
|
||||
<label class="checkbox-inline"><input type="checkbox" name="visu_tele" value="${rapport.visu_tele}"
|
||||
tal:attributes="checked rapport.visu_tele != 0 and 'checked' or None">Endoscope à fibre optique</label>
|
||||
tal:attributes="checked rapport.visu_tele != 0 and 'checked' or None">Inspection télévisuelle</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label">Recherche de réseaux : </label>
|
||||
@@ -363,7 +363,7 @@
|
||||
<br/>
|
||||
<div class="form-group">
|
||||
<div class="form-group">
|
||||
<a class="btn btn-default" href="${request.route_url('dossier_view', nodossier=nodossier)}">
|
||||
<a class="btn btn-default" href="${request.route_url('dossier_view', nodossier=nodossier) + '#tab_rdf'}">
|
||||
<span class="glyphicon glyphicon-arrow-left"></span> Annuler</a>
|
||||
<button class="btn btn-primary" type="submit" name="form.submitted">
|
||||
<span class="glyphicon glyphicon-ok"></span> Enregistrer</button>
|
||||
|
||||
79
mondumas/templates/dossier/rdf_nochantier.pt
Normal file
79
mondumas/templates/dossier/rdf_nochantier.pt
Normal file
@@ -0,0 +1,79 @@
|
||||
<metal:block use-macro="main_template">
|
||||
<div metal:fill-slot="content">
|
||||
|
||||
<div tal:condition="message" tal:content="message" class="alert alert-danger" />
|
||||
<br />
|
||||
|
||||
<div class="row">
|
||||
<form id="rdf_nochantier-form" class="form-horizontal" action="${url}" method="post"
|
||||
data-fv-framework="bootstrap"
|
||||
data-fv-icon-valid="glyphicon glyphicon-ok"
|
||||
data-fv-icon-invalid="glyphicon glyphicon-remove"
|
||||
data-fv-icon-validating="glyphicon glyphicon-refresh">
|
||||
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-3">N° rapport</label>
|
||||
<div class="col-sm-9">
|
||||
<p class="form-control-static">${rapport.no_id}</p>
|
||||
</div>
|
||||
<label class="control-label col-sm-3">Date d'intervention</label>
|
||||
<div class="col-sm-9">
|
||||
<p class="form-control-static">${rapport.date_inter.strftime('%d-%m-%Y')}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-3">Nom du chantier actuel</label>
|
||||
<div class="col-sm-9">
|
||||
<p class="form-control-static text-danger">${rapport.C_QUALITE} ${rapport.C_NOM}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-3">No du chantier actuel</label>
|
||||
<div class="col-sm-9">
|
||||
<p class="form-control-static">${rapport.nochantier}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">Nouveau no de chantier</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" class="form-control" id="new_nochantier" name="new_nochantier"
|
||||
placeholder="6 chiffres"
|
||||
data-fv-numeric="true"
|
||||
data-fv-numeric-message="Le numero de dossier est incorrect" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-3 col-sm-9">
|
||||
<a class="btn btn-default" href="${request.route_url('rdf_view', no_id=norapport)}">
|
||||
<span class="glyphicon glyphicon-arrow-left"></span> Annuler</a>
|
||||
<button class="btn btn-primary" type="submit" name="form.submitted">
|
||||
<span class="glyphicon glyphicon-ok"></span> Enregistrer</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div> <!-- row -->
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
|
||||
</div>
|
||||
<div metal:fill-slot="additional_scripts">
|
||||
<!-- autocomplete plugin -->
|
||||
<link href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css" rel="stylesheet">
|
||||
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
$('#rdf_nochantier-form').formValidation({
|
||||
framework: 'bootstrap',
|
||||
excluded: ':disabled',
|
||||
icon: {
|
||||
valid: 'glyphicon glyphicon-ok',
|
||||
invalid: 'glyphicon glyphicon-remove',
|
||||
validating: 'glyphicon glyphicon-refresh'
|
||||
},
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
</metal:block>
|
||||
@@ -15,9 +15,13 @@
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="control-label">N° dossier</label> : ${nodossier}<br />
|
||||
<label class="control-label">N° rapport</label> : ${norapport}<br />
|
||||
<label class="control-label">Date d'intervention</label> : ${rapport.date_inter.strftime('%d-%m-%Y')}<br />
|
||||
<label class="control-label">N° dossier</label> : ${nodossier}<br />
|
||||
<p tal:condition="pt_name != 'rdf_rapport'">
|
||||
<a href="${request.route_url('rdf_nochantier', no_id=norapport)}"
|
||||
tal:condition="access > 0">[ Changer le No dossier ]</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<h4 class="text-center text-primary">ADRESSE d'INTERVENTION</h4>
|
||||
@@ -201,7 +205,7 @@
|
||||
<label class="checkbox-inline" disabled><input type="checkbox" name="visu_endoscope" value="${rapport.visu_endoscope}" disabled
|
||||
tal:attributes="checked rapport.visu_endoscope != 0 and 'checked' or None">Endoscope à fibre optique</label>
|
||||
<label class="checkbox-inline" disabled><input type="checkbox" name="visu_tele" value="${rapport.visu_tele}" disabled
|
||||
tal:attributes="checked rapport.visu_tele != 0 and 'checked' or None">Endoscope à fibre optique</label>
|
||||
tal:attributes="checked rapport.visu_tele != 0 and 'checked' or None">Inspection télévisuelle</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label">Recherche de réseaux : </label>
|
||||
@@ -348,7 +352,7 @@
|
||||
<br/>
|
||||
<div class="form-group" tal:condition="pt_name=='rdf_view'">
|
||||
<div class="form-group">
|
||||
<a class="btn btn-default" href="${request.route_url('dossier_view', nodossier=nodossier)}">
|
||||
<a class="btn btn-default" href="${request.route_url('dossier_view', nodossier=nodossier)#tab_rdf}">
|
||||
<span class="glyphicon glyphicon-arrow-left"></span> Retour Dossier</a>
|
||||
<a class="btn btn-primary" href="/rdf_edit/${nodossier}/${rapport.date_inter.strftime('%Y-%m-%d')}"
|
||||
tal:condition="date_facture=='' or access>=8">
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
<div class="form-group">
|
||||
<a class="btn btn-default" href="${request.route_url('dossier_view', nodossier=nodossier)}">
|
||||
<span class="glyphicon glyphicon-arrow-left"></span> Annuler</a>
|
||||
<button class="btn btn-primary" type="submit" name="form.submitted">
|
||||
<button class="btn btn-primary" type="submit" name="form.submitted" tal:condition="access > 1">
|
||||
<span class="glyphicon glyphicon-ok"></span> Enregistrer</button>
|
||||
<button class="btn btn-danger" type="submit" name="form.deleted"
|
||||
tal:condition="nolig != '0' and logged_in.upper()==suivi.USERMAJ">
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<input id="uploadfile" name="files" type="file" value="" required multiple />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<a class="btn btn-default" href="${request.route_url('dossier_view', nodossier=nodossier)}">
|
||||
<a class="btn btn-default" href="${request.route_url('dossier_view', nodossier=nodossier) + '#tab_attaches'}">
|
||||
<span class="glyphicon glyphicon-arrow-left"></span> Retour au dossier</a>
|
||||
<button id="uploadButton" class="btn btn-primary" type="submit" name="form.submitted">
|
||||
<i class="glyphicon glyphicon-refresh"></i> Télécharger</button>
|
||||
|
||||
@@ -9,6 +9,14 @@
|
||||
data-fv-icon-invalid="glyphicon glyphicon-remove"
|
||||
data-fv-icon-validating="glyphicon glyphicon-refresh">
|
||||
|
||||
<p>
|
||||
Les ordres de missions doivent être des <span class="text-danger"><b>fichiers PDF originaux téléchargés des portails des Assureurs</b></span>. :
|
||||
</p>
|
||||
<ul>
|
||||
<li>Portail d'AXA</li>
|
||||
<li>Portail Sinapps (AXA et MAIF)</li>
|
||||
</ul>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="control-label" for="uploadfile">Veuillez séléctionner un fichier :</label>
|
||||
<input id="uploadfile" name="filename" type="file" value="" required />
|
||||
@@ -24,7 +32,7 @@
|
||||
</div>
|
||||
</form>
|
||||
<ul>
|
||||
<li>Seuls les documents au format <b>PDF</b> seront acceptés.</li>
|
||||
<li>Seuls les documents au format <b>PDF ORIGINAUX</b> seront acceptés.</li>
|
||||
<li>La taille de chaque document ne doit <b>pas dépasser 4 Mo</b>.</li>
|
||||
</ul>
|
||||
|
||||
|
||||
@@ -13,6 +13,18 @@
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<h2>Dernières modifications</h2>
|
||||
<table class="table table-condensed">
|
||||
<tr tal:repeat="item last_modified">
|
||||
<td>${item.nomtable}</td>
|
||||
<td>${item.societe}</td>
|
||||
<td>${item.no_id}</td>
|
||||
<td>${item.datemaj.strftime('%d %b, %H:%M')}</td>
|
||||
<td>${item.usermaj}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
</div><!-- content -->
|
||||
</metal:block>
|
||||
|
||||
@@ -1,124 +0,0 @@
|
||||
<metal:block use-macro="main_template">
|
||||
<div metal:fill-slot="content">
|
||||
|
||||
<h3 class="text-center">POSTES de TRAVAIL</h3>
|
||||
<br />
|
||||
<div id="jquery" class="container-fluid">
|
||||
<div class="row text-center">
|
||||
<div class="col-sm-3">
|
||||
<span class="fas fa-desktop logo-primary"></span>
|
||||
<h4>15 ordinateurs</h4>
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
<span class="fas fa-laptop logo-primary"></span>
|
||||
<h4>1 portable</h4>
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
<span class="fas fa-print logo-primary"></span>
|
||||
<h4>4 imprimantes réseau</h4>
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
<span class="fas fa-tablet-alt logo-small"></span>
|
||||
<h4>10 tablettes</h4>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
|
||||
<h3 class="text-center">SERVEURS WINDOWS</h3>
|
||||
<div id="jquery" class="container-fluid">
|
||||
<div class="row text-center">
|
||||
<div class="col-sm-3">
|
||||
<span class="fas fa-server logo-small"></span>
|
||||
<h4>SRV2012</h4>
|
||||
Serveur HYPER-V
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
<span class="fas fa-hdd logo-primary"></span>
|
||||
<h4>SRVTSE</h4>
|
||||
Serveur Accès à Distance
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
<span class="fas fa-hdd logo-primary"></span>
|
||||
<h4>SRVBD / DC</h4>
|
||||
Serveur Base de données
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
<span class="far fa-hdd logo-primary"></span>
|
||||
<h4>SECOURS</h4>
|
||||
Serveur de secours
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br />
|
||||
<h3 class="text-center">SERVEUR LINUX</h3>
|
||||
<div id="jquery" class="container-fluid">
|
||||
<div class="row text-center">
|
||||
<div class="col-sm-12">
|
||||
<span class="fas fa-hdd logo-small"></span>
|
||||
<h4>SRVWEB</h4>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
|
||||
<h3 class="text-center">CONNECTION INTERNET</h3>
|
||||
<div id="jquery" class="container-fluid">
|
||||
<div class="row text-center">
|
||||
<div class="col-sm-12">
|
||||
<span class="fas fa-globe logo-primary"></span>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<div class="row text-center">
|
||||
<div class="col-sm-3">
|
||||
<h4>Routeur SFR Fibre 10 Mbps</h4>
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
<span class="far fa-hdd logo-small"></span>
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
<span class="far fa-hdd logo-small"></span>
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
<h4>Router ORANGE ADSL2</h4>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<div class="row text-center">
|
||||
<div class="col-sm-12">
|
||||
<span class="far fa-hdd logo-small"></span>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<br />
|
||||
<div class="row text-center">
|
||||
<div class="col-sm-3">
|
||||
<span class="fas fa-server logo-primary"></span>
|
||||
<h4>Serveurs</h4>
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
<span class="fas fa-desktop logo-primary"></span>
|
||||
<h4>Ordinateurs</h4>
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
<span class="fas fa-print logo-primary"></span>
|
||||
<h4>Imprimantes</h4>
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
<span class="fas fa-wifi logo-primary"></span>
|
||||
<h4>Bornes Wifi</h4>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<br />
|
||||
|
||||
</div><!-- content -->
|
||||
<div metal:fill-slot="additional_scripts">
|
||||
<script src="https://kit.fontawesome.com/15d9c0c809.js" crossorigin="anonymous"></script>
|
||||
</div>
|
||||
</metal:block>
|
||||
|
||||
|
||||
@@ -34,10 +34,6 @@
|
||||
<a href="${request.application_url}/text_list"><span class="glyphicon glyphicon-list logo-primary"></span></a>
|
||||
<h4>TEXTES EMAIL</h4>
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
<a href="${request.application_url}/new_home"><span class="glyphicon glyphicon-dashboard logo-primary"></span></a>
|
||||
<h4>PROTOTYPE HOME</h4>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<div class="row text-center">
|
||||
@@ -54,16 +50,10 @@
|
||||
<h4>CONNEXIONS</h4>
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
<a href="${request.application_url}/stats_dd/PE"><span class="glyphicon glyphicon-wrench logo-primary"></span></a>
|
||||
<a href="${request.application_url}/stats"><span class="glyphicon glyphicon-wrench logo-primary"></span></a>
|
||||
<h4>STATS GLOBALES</h4>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row text-center">
|
||||
<div class="col-sm-3">
|
||||
<a href="${request.application_url}/infrastructure"><span class="glyphicon glyphicon-cog logo-primary"></span></a>
|
||||
<h4>INFRASTRUCTURE</h4>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<br />
|
||||
</div>
|
||||
|
||||
@@ -58,12 +58,15 @@
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-xs-3 control-label" for="email_from">Email</label>
|
||||
<div class="col-xs-8">
|
||||
<div class="col-xs-5">
|
||||
<input class="form-control" type="text" name="email_from" value="${societe.email_from}"
|
||||
placeholder="45 caractères maximum"
|
||||
data-fv-emailaddress="true"
|
||||
data-fv-emailaddress-message="L'adresse email n'est pas valide" />
|
||||
</div>
|
||||
<div class="col-xs-3">
|
||||
<input class="form-control" type="text" name="email_cci" value="${societe.email_cci}" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-xs-3 control-label" for="Corresp1">Horiares</label>
|
||||
|
||||
@@ -26,9 +26,10 @@
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Référence</th>
|
||||
<th>Fam</th>
|
||||
<th>Libellé</th>
|
||||
<th>Unité</th>
|
||||
<th class="text-right">Prix HT 1</th>
|
||||
<th class="text-right">Prix HT</th>
|
||||
<th>Unit</th>
|
||||
<th>Modif le</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
</div>
|
||||
<br />
|
||||
<div class="form-group">
|
||||
<a class="btn btn-default" href="/">
|
||||
<a class="btn btn-default" href="/tarifs/AXA">
|
||||
<span class="glyphicon glyphicon-arrow-left"></span> Retour</a>
|
||||
<button id="uploadButton" class="btn btn-primary" type="submit" name="form.submitted">
|
||||
<i class="glyphicon glyphicon-refresh"></i> Importer</button>
|
||||
|
||||
@@ -48,7 +48,8 @@
|
||||
<div class="col-sm-9">
|
||||
<select class="form-control" id="access" name="access">
|
||||
<div tal:repeat="item access">
|
||||
<option value="${item}" tal:attributes="selected str(individu.access)==item[0] and 'selected' or None">${item}</option>
|
||||
<option value="${item.code}" tal:attributes="selected individu.access==item.code and 'selected' or None">
|
||||
${item.code} | ${item.libelle}</option>
|
||||
</div>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
@@ -63,14 +63,17 @@
|
||||
|
||||
var options_ca_3y_1 = {
|
||||
title: '${title1}',
|
||||
colors: ['0099c6', '990099', '109618', 'ff9900', '#dc3912', '3366cc'],
|
||||
};
|
||||
|
||||
var options_ca_3y_2 = {
|
||||
title: '${title2}',
|
||||
colors: ['0099c6', '990099', '109618', 'ff9900', '#dc3912', '3366cc'],
|
||||
};
|
||||
|
||||
var options_ca_3y_3 = {
|
||||
title: '${title3}',
|
||||
colors: ['0099c6', '990099', '109618', 'ff9900', '#dc3912', '3366cc'],
|
||||
};
|
||||
|
||||
var chart_ca_12m = new google.visualization.ColumnChart(document.getElementById('chart_ca_12m'));
|
||||
|
||||
@@ -37,6 +37,20 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2>Evolution du CA global sur les 3 dernières années</h2>
|
||||
<div class="row">
|
||||
<!-- camembert 1 -->
|
||||
<div class="col-sm-4">
|
||||
<div id="chart_ca_3y_1_x" style="width: 100%; height: 500px;"></div>
|
||||
</div>
|
||||
<div class="col-sm-4">
|
||||
<div id="chart_ca_3y_2_x" style="width: 100%; height: 500px;"></div>
|
||||
</div>
|
||||
<div class="col-sm-4">
|
||||
<div id="chart_ca_3y_3_x" style="width: 100%; height: 500px;"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
|
||||
<script type="text/javascript">
|
||||
@@ -48,6 +62,10 @@
|
||||
var dataSet_ca_3y_2 = ${chart_ca_3y_2};
|
||||
var dataSet_ca_3y_3 = ${chart_ca_3y_3};
|
||||
|
||||
var dataSet_ca_3y_1_x = ${chart_ca_3y_1_x};
|
||||
var dataSet_ca_3y_2_x = ${chart_ca_3y_2_x};
|
||||
var dataSet_ca_3y_3_x = ${chart_ca_3y_3_x};
|
||||
|
||||
function drawChart() {
|
||||
var data_ca_12m = google.visualization.arrayToDataTable(dataSet_ca_12m);
|
||||
|
||||
@@ -55,6 +73,10 @@
|
||||
var data_ca_3y_2 = google.visualization.arrayToDataTable(dataSet_ca_3y_2);
|
||||
var data_ca_3y_3 = google.visualization.arrayToDataTable(dataSet_ca_3y_3);
|
||||
|
||||
var data_ca_3y_1_x = google.visualization.arrayToDataTable(dataSet_ca_3y_1_x);
|
||||
var data_ca_3y_2_x = google.visualization.arrayToDataTable(dataSet_ca_3y_2_x);
|
||||
var data_ca_3y_3_x = google.visualization.arrayToDataTable(dataSet_ca_3y_3_x);
|
||||
|
||||
var options_ca_12m = {
|
||||
title: '${title}',
|
||||
vAxis: {title: "Chiffre d'Affaires en €"},
|
||||
@@ -62,15 +84,33 @@
|
||||
};
|
||||
|
||||
var options_ca_3y_1 = {
|
||||
title: '${title1}',
|
||||
title: 'CA groupe ${annee1}',
|
||||
pieHole: 0.3,
|
||||
};
|
||||
|
||||
var options_ca_3y_2 = {
|
||||
title: '${title2}',
|
||||
title: 'CA groupe ${annee2}',
|
||||
pieHole: 0.3,
|
||||
};
|
||||
|
||||
var options_ca_3y_3 = {
|
||||
title: '${title3}',
|
||||
title: 'CA groupe ${annee3}',
|
||||
pieHole: 0.3,
|
||||
};
|
||||
|
||||
var options_ca_3y_1_x = {
|
||||
title: 'CA global ${annee1}',
|
||||
pieHole: 0.3,
|
||||
};
|
||||
|
||||
var options_ca_3y_2_x = {
|
||||
title: 'CA global ${annee2}',
|
||||
pieHole: 0.3,
|
||||
};
|
||||
|
||||
var options_ca_3y_3_x = {
|
||||
title: 'CA global ${annee3}',
|
||||
pieHole: 0.3,
|
||||
};
|
||||
|
||||
var chart_ca_12m = new google.visualization.ColumnChart(document.getElementById('chart_ca_12m'));
|
||||
@@ -82,6 +122,13 @@
|
||||
chart_ca_3y_2.draw(data_ca_3y_2, options_ca_3y_2);
|
||||
var chart_ca_3y_3 = new google.visualization.PieChart(document.getElementById('chart_ca_3y_3'));
|
||||
chart_ca_3y_3.draw(data_ca_3y_3, options_ca_3y_3);
|
||||
|
||||
var chart_ca_3y_1_x = new google.visualization.PieChart(document.getElementById('chart_ca_3y_1_x'));
|
||||
chart_ca_3y_1_x.draw(data_ca_3y_1_x, options_ca_3y_1_x);
|
||||
var chart_ca_3y_2_x = new google.visualization.PieChart(document.getElementById('chart_ca_3y_2_x'));
|
||||
chart_ca_3y_2_x.draw(data_ca_3y_2_x, options_ca_3y_2_x);
|
||||
var chart_ca_3y_3_x = new google.visualization.PieChart(document.getElementById('chart_ca_3y_3_x'));
|
||||
chart_ca_3y_3_x.draw(data_ca_3y_3_x, options_ca_3y_3_x);
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
|
||||
var options_delais_p = {
|
||||
title: '${title}',
|
||||
pieHole: 0.3,
|
||||
};
|
||||
|
||||
var chart_delais_p = new google.visualization.PieChart(document.getElementById('chart_delais_p'));
|
||||
|
||||
103
mondumas/templates/stats/pourcentage_devis.pt
Normal file
103
mondumas/templates/stats/pourcentage_devis.pt
Normal file
@@ -0,0 +1,103 @@
|
||||
<metal:block use-macro="main_template">
|
||||
<div metal:fill-slot="content">
|
||||
|
||||
<br />
|
||||
<div class="row">
|
||||
<form method="POST" id="frm" class="form-horizontal">
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2">Societe</label>
|
||||
<div class="col-sm-4">
|
||||
<select class="form-control" id="societe" name="societe" onChange="$('#frm').submit()">
|
||||
<tal:block tal:repeat="item societes">
|
||||
<option value="${item}" tal:attributes="selected societe==item and 'selected' or None"> ${item}</option>
|
||||
</tal:block>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<!-- CAMENBERT DU NOMBRE DE DEVIS FACTURES -->
|
||||
<div class="col-sm-4"><div id="chart_devis_y1" style="width: 100%; height: 500px;"></div></div>
|
||||
<div class="col-sm-4"><div id="chart_devis_y2" style="width: 100%; height: 500px;"></div></div>
|
||||
<div class="col-sm-4"><div id="chart_devis_y3" style="width: 100%; height: 500px;"></div></div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<!-- CAMEMBERT DU NOMBRE DE FACTURES AVEC DEVIS -->
|
||||
<div class="col-sm-4"><div id="chart_fact_y1" style="width: 100%; height: 500px;"></div></div>
|
||||
<div class="col-sm-4"><div id="chart_fact_y2" style="width: 100%; height: 500px;"></div></div>
|
||||
<div class="col-sm-4"><div id="chart_fact_y3" style="width: 100%; height: 500px;"></div></div>
|
||||
</div>
|
||||
|
||||
|
||||
<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
|
||||
<script type="text/javascript">
|
||||
google.charts.load("current", {packages:["corechart"]});
|
||||
google.charts.setOnLoadCallback(drawChart);
|
||||
|
||||
var dataSet_devis_y1 = ${chart_devis_y1};
|
||||
var dataSet_devis_y2 = ${chart_devis_y2};
|
||||
var dataSet_devis_y3 = ${chart_devis_y3};
|
||||
|
||||
var dataSet_fact_y1 = ${chart_fact_y1};
|
||||
var dataSet_fact_y2 = ${chart_fact_y2};
|
||||
var dataSet_fact_y3 = ${chart_fact_y3};
|
||||
|
||||
function drawChart() {
|
||||
|
||||
var data_devis_y1 = google.visualization.arrayToDataTable(dataSet_devis_y1);
|
||||
var data_devis_y2 = google.visualization.arrayToDataTable(dataSet_devis_y2);
|
||||
var data_devis_y3 = google.visualization.arrayToDataTable(dataSet_devis_y3);
|
||||
|
||||
var data_fact_y1 = google.visualization.arrayToDataTable(dataSet_fact_y1);
|
||||
var data_fact_y2 = google.visualization.arrayToDataTable(dataSet_fact_y2);
|
||||
var data_fact_y3 = google.visualization.arrayToDataTable(dataSet_fact_y3);
|
||||
|
||||
var options_devis_y1 = {
|
||||
title: '${title_devis1}',
|
||||
pieHole: 0.3,
|
||||
};
|
||||
var options_devis_y2 = {
|
||||
title: '${title_devis2}',
|
||||
pieHole: 0.3,
|
||||
};
|
||||
var options_devis_y3 = {
|
||||
title: '${title_devis3}',
|
||||
pieHole: 0.3,
|
||||
};
|
||||
|
||||
var options_fact_y1 = {
|
||||
title: '${title_fact1}',
|
||||
pieHole: 0.3,
|
||||
};
|
||||
var options_fact_y2 = {
|
||||
title: '${title_fact2}',
|
||||
pieHole: 0.3,
|
||||
};
|
||||
var options_fact_y3 = {
|
||||
title: '${title_fact3}',
|
||||
pieHole: 0.3,
|
||||
};
|
||||
|
||||
var chart_devis_y1 = new google.visualization.PieChart(document.getElementById('chart_devis_y1'));
|
||||
chart_devis_y1.draw(data_devis_y1, options_devis_y1);
|
||||
var chart_devis_y2 = new google.visualization.PieChart(document.getElementById('chart_devis_y2'));
|
||||
chart_devis_y2.draw(data_devis_y2, options_devis_y2);
|
||||
var chart_devis_y3 = new google.visualization.PieChart(document.getElementById('chart_devis_y3'));
|
||||
chart_devis_y3.draw(data_devis_y3, options_devis_y3);
|
||||
|
||||
var chart_fact_y1 = new google.visualization.PieChart(document.getElementById('chart_fact_y1'));
|
||||
chart_fact_y1.draw(data_fact_y1, options_fact_y1);
|
||||
var chart_fact_y2 = new google.visualization.PieChart(document.getElementById('chart_fact_y2'));
|
||||
chart_fact_y2.draw(data_fact_y2, options_fact_y2);
|
||||
var chart_fact_y3 = new google.visualization.PieChart(document.getElementById('chart_fact_y3'));
|
||||
chart_fact_y3.draw(data_fact_y3, options_fact_y3);
|
||||
}
|
||||
</script>
|
||||
|
||||
</div><!-- content -->
|
||||
</metal:block>
|
||||
|
||||
|
||||
@@ -28,7 +28,12 @@
|
||||
<a href="${request.application_url}/ca_clients/PE" tal:condition="access > 0">
|
||||
<span class="glyphicon glyphicon-equalizer logo-warning"></span>
|
||||
<h4>CA / TYPE CLIENTS</h4></a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
<a href="${request.application_url}/pourcentage_devis/PE" tal:condition="access > 0">
|
||||
<span class="glyphicon glyphicon-equalizer logo-warning"></span>
|
||||
<h4>NB DEVIS/FACTURES</h4></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -32,6 +32,9 @@ def rdv_edit(request):
|
||||
url = request.route_url("rdv_edit", nodossier=nodossier, nolig=nolig)
|
||||
message = ''
|
||||
|
||||
# lire son niveau d'accès
|
||||
member = get_member_by_id(request, logged_in)
|
||||
access = member.access
|
||||
# lire la liste des users avec agenda
|
||||
agendas = get_users_agenda(request, '')
|
||||
# liste des types de rdv
|
||||
@@ -86,6 +89,7 @@ def rdv_edit(request):
|
||||
'agendas': agendas,
|
||||
'rdv': rdv,
|
||||
'message': message,
|
||||
'access': access,
|
||||
}
|
||||
|
||||
@view_config(route_name='agenda', renderer='../templates/agenda/agenda.pt', permission='view')
|
||||
|
||||
@@ -1,465 +1,435 @@
|
||||
# -*- coding: utf8 -*-
|
||||
from pyramid.response import Response
|
||||
from pyramid.renderers import render, get_renderer
|
||||
from pyramid.view import (
|
||||
view_config,
|
||||
forbidden_view_config,
|
||||
)
|
||||
from pyramid.security import (
|
||||
remember,
|
||||
forget,
|
||||
)
|
||||
from pyramid.httpexceptions import (
|
||||
HTTPFound,
|
||||
HTTPNotFound,
|
||||
HTTPForbidden,
|
||||
)
|
||||
from datetime import *
|
||||
from sqlalchemy.exc import DBAPIError
|
||||
from ..security import groupfinder
|
||||
from user_agents import parse
|
||||
|
||||
import json
|
||||
import locale
|
||||
import hashlib
|
||||
import imaplib
|
||||
import email
|
||||
|
||||
from ..models.default import *
|
||||
from ..models.agenda import *
|
||||
from ..models.dossier import (
|
||||
get_chantiers_byName,
|
||||
get_clients_byName
|
||||
)
|
||||
|
||||
from ..views.utils import *
|
||||
|
||||
def to_decimal(x):
|
||||
import decimal
|
||||
return decimal.Decimal(str(x))
|
||||
|
||||
def to_euro(x):
|
||||
"""Takes a float and returns 12 345,67 €"""
|
||||
locale.setlocale(locale.LC_ALL,'')
|
||||
return locale.currency(x, True, True)
|
||||
|
||||
def to_euroz(x):
|
||||
"""Takes a float and returns 12 345,67 € if not zero"""
|
||||
if x == 0:
|
||||
return ''
|
||||
else:
|
||||
return to_euro(x)
|
||||
|
||||
def to_decz(x):
|
||||
"""Takes a float and returns a number with 2 dec"""
|
||||
locale.setlocale(locale.LC_ALL,'')
|
||||
if x == 0:
|
||||
return ""
|
||||
else:
|
||||
return locale.format_string('%.2f',x, False)
|
||||
|
||||
def to_sha1(message):
|
||||
return hashlib.sha1(message.encode('utf-8')).hexdigest()
|
||||
|
||||
def to_int(x):
|
||||
try:
|
||||
number = int(x.replace(',', '.'))
|
||||
return number
|
||||
except ValueError:
|
||||
return 0
|
||||
|
||||
def to_percent(x):
|
||||
"""Takes a float and returns a string"""
|
||||
return ("%.2f " % x).replace('.', ',') + "%"
|
||||
|
||||
|
||||
@view_config(route_name='home', renderer='../templates/default/home.pt', permission='view')
|
||||
def home(request):
|
||||
logged_in = request.authenticated_userid.upper()
|
||||
# lire la fiche de l'utilisateur
|
||||
member = get_member_by_id(request, logged_in)
|
||||
access = member.access
|
||||
|
||||
return {
|
||||
'page_title': 'Bienvenue sur %s' % request.host,
|
||||
'project': 'mondumas',
|
||||
'access': access,
|
||||
'logged_in': logged_in,
|
||||
}
|
||||
|
||||
def mailbox_connect(request, societe):
|
||||
# connecter au serveur IMAP de la societe
|
||||
if societe == 'PE':
|
||||
mbx_name = 'peinture-dumas@entreprise-dumas.com'
|
||||
mbx_pwd = 'sasdumas'
|
||||
elif societe == 'ME':
|
||||
mbx_name = 'menuiserie-dumas@entreprise-dumas.com'
|
||||
mbx_pwd = 'sasdumas'
|
||||
elif societe == 'PL':
|
||||
mbx_name = 'versanit-dumas@entreprise-dumas.com'
|
||||
mbx_pwd = 'sasdumas'
|
||||
elif societe == 'PO':
|
||||
mbx_name = 'polynet-dumas@entreprise-dumas.com'
|
||||
mbx_pwd = 'sasdumas'
|
||||
else:
|
||||
request.session.flash("Cette société est inconnue ou non traitée : %s" % societe, 'danger')
|
||||
return None
|
||||
|
||||
conn = imaplib.IMAP4_SSL('imap.entreprise-dumas.com')
|
||||
try:
|
||||
# se connecter à la mailbox
|
||||
conn.login(mbx_name, mbx_pwd)
|
||||
except imaplib.IMAP4.error:
|
||||
request.session.flash("ERREUR connexion au compte %s" % mbx_name, 'danger')
|
||||
return None
|
||||
|
||||
return conn
|
||||
|
||||
@view_config(route_name='new_home', renderer='../templates/default/new_home.pt', permission='view')
|
||||
def new_home(request):
|
||||
logged_in = request.authenticated_userid.upper()
|
||||
url = request.route_url('new_home')
|
||||
# lire la fiche de l'utilisateur
|
||||
member = get_member_by_id(request, logged_in)
|
||||
access = member.access
|
||||
agenda = member.agenda
|
||||
|
||||
datedeb = date.today().strftime("%Y-%m-%d")
|
||||
|
||||
nb_dd_restants = get_dd_restant(request)
|
||||
nb_de_restants = get_de_restant(request)
|
||||
nb_fa_restants = get_fa_restant(request)
|
||||
|
||||
nb_rdv = get_rdv_by_date(request, datedeb, agenda)
|
||||
nb_rdf = get_rdf_null(request)
|
||||
|
||||
# Récupération de la listes des mails pour ensuite avoir leur nombre
|
||||
def demandes_lister(societe, search_criteria):
|
||||
# connecter au serveur de mail
|
||||
conn = mailbox_connect(request, societe)
|
||||
# select INBOX
|
||||
rv, data = conn.select('INBOX', readonly =True)
|
||||
|
||||
# créer la liste des entêtes des messages à afficher
|
||||
liste = []
|
||||
for criteria in search_criteria:
|
||||
rv, data = conn.search(None, criteria)
|
||||
if rv != 'OK':
|
||||
request.session.flash("ERREUR de lecture de la boîte de réception", 'danger')
|
||||
return HTTPFound(location=request.route_url('home'))
|
||||
|
||||
mail_ids = data[0]
|
||||
for email_UID in mail_ids.split():
|
||||
rv, msg_data = conn.fetch(email_UID, '(RFC822)')
|
||||
if rv != 'OK':
|
||||
request.session.flash("ERREUR de lecture du message %s" % email_UID, 'danger')
|
||||
return HTTPFound(location=request.route_url('home'))
|
||||
|
||||
msg = email.message_from_bytes(msg_data[0][1])
|
||||
hdr = email.header.make_header(email.header.decode_header(msg['Subject']))
|
||||
email_subject = str(hdr)
|
||||
email_from = email.utils.parseaddr(msg['from'])[1]
|
||||
# Now convert to local date-time
|
||||
date_tuple = email.utils.parsedate_tz(msg['Date'])
|
||||
if date_tuple:
|
||||
email_date = datetime.fromtimestamp(email.utils.mktime_tz(date_tuple))
|
||||
else:
|
||||
email_date = datetime.now()
|
||||
|
||||
d = {
|
||||
"email_societe": societe,
|
||||
"email_date": email_date,
|
||||
"email_from": email_from.split('@')[1],
|
||||
'email_subject':email_subject,
|
||||
"email_uid": email_UID
|
||||
}
|
||||
liste.append(d)
|
||||
|
||||
# deconnexion du serveur
|
||||
conn.close()
|
||||
conn.logout()
|
||||
return liste
|
||||
|
||||
societes = ['PE','ME','PL','PO']
|
||||
|
||||
# critères de recherche des demandes d'interventions de la MAIF
|
||||
# search_criteria = ['FROM service.sinistres@domus-services.fr SUBJECT "Ordre de mission DOMUS - Dossier" UNDELETED'] "
|
||||
search_criteria = ['FROM gestionsinistre@maif.fr SUBJECT "Intervention entreprise partenaire"',
|
||||
'FROM service.sinistres@domus-services.fr UNDELETED']
|
||||
# 'FROM service.sinistres@domus-services.fr SUBJECT "Ordre de mission DOMUS - Dossier" UNDELETED']
|
||||
|
||||
emails=[]
|
||||
# lister les demandes par societe
|
||||
for societe in societes:
|
||||
emails = emails + demandes_lister(societe, search_criteria)
|
||||
|
||||
# messages lus
|
||||
msglus = bool(emails)
|
||||
|
||||
return {
|
||||
'page_title': 'Bienvenue sur %s' % request.host,
|
||||
'project': 'mondumas',
|
||||
'access': access,
|
||||
'logged_in': logged_in,
|
||||
'nb_dd_restants': nb_dd_restants,
|
||||
'nb_de_restants': nb_de_restants,
|
||||
'nb_fa_restants': nb_fa_restants,
|
||||
'nb_rdv': nb_rdv,
|
||||
'nb_rdf': nb_rdf,
|
||||
'nb_mails': len(emails),
|
||||
#'mails': emails,
|
||||
}
|
||||
|
||||
@view_config(route_name='envoyer_mdp', renderer='../templates/default/envoyer_mdp.pt')
|
||||
def envoyer_mdp(request):
|
||||
url = request.route_url('envoyer_mdp')
|
||||
message = ''
|
||||
|
||||
if 'form.submitted' in request.params:
|
||||
login = request.params['login']
|
||||
member = get_member_by_id(request, login)
|
||||
if member:
|
||||
# Fabrication du corps du email_passwordMessage
|
||||
lien = update_membre_mdp_oublie(request, login)
|
||||
body = """
|
||||
<p>Bonjour,</p>
|
||||
|
||||
<p>Le lien suivant vous dirigera vers une page où vous pourrez ré-initialiser votre mot de passe d'accès à <b>gestion.entreprise-dumas.com</b>:</p>
|
||||
|
||||
<p>%s</p>
|
||||
|
||||
<p>(Ce lien est valide pendant 168 heures.</p>
|
||||
|
||||
|
||||
<p>
|
||||
Cordialement,<br />
|
||||
gestion.entreprise-dumas.com
|
||||
</p>
|
||||
""" % (request.route_url('redefinir_mdp', lien=lien))
|
||||
# envoyer l'email
|
||||
expediteur = request.registry.settings['mondumas.admin_email']
|
||||
send_mail(request, expediteur, [member.email,], "[Ent. Dumas] Demande de ré-initialisation du mot de passe", body)
|
||||
|
||||
request.session.flash("Le lien permettant de redéfinir votre mot de passe vous a été envoyé à l'adresse : %s." % member.email, 'success')
|
||||
return HTTPFound(location=request.route_url('affiche_message', login=login))
|
||||
else:
|
||||
message = "Le mot de passe fourni est incorrect."
|
||||
return {
|
||||
'page_title': "Changer mon mot de passe",
|
||||
'url': url,
|
||||
'message': message,
|
||||
}
|
||||
|
||||
@view_config(route_name='changer_mdp', renderer='../templates/default/changer_mdp.pt', permission='view')
|
||||
def changer_mdp(request):
|
||||
url = request.route_url('changer_mdp')
|
||||
logged_in = request.authenticated_userid
|
||||
message = ''
|
||||
|
||||
member = get_member_by_id(request, logged_in)
|
||||
if member:
|
||||
if 'form.submitted' in request.params:
|
||||
old_password = request.params['old_password']
|
||||
new_password = request.params['new_password1']
|
||||
if member.mdp == to_sha1(old_password):
|
||||
update_membre_mdp(request, logged_in, new_password)
|
||||
request.session.flash("Votre mot de passe a été mis à jour avec succès.")
|
||||
return HTTPFound(location=request.route_url('home'))
|
||||
else:
|
||||
message = "Le mot de passe actuel n'est pas correct."
|
||||
|
||||
return {
|
||||
'page_title': "Changer mon mot de passe",
|
||||
'url': url,
|
||||
'member': member,
|
||||
'message': message,
|
||||
}
|
||||
|
||||
@view_config(route_name='redefinir_mdp', renderer='../templates/default/redefinir_mdp.pt')
|
||||
@view_config(route_name='init_mdp', renderer='../templates/default/redefinir_mdp.pt')
|
||||
def redefinir_mdp(request):
|
||||
if request.matched_route.name == 'redefinir_mdp':
|
||||
lien = request.matchdict["lien"]
|
||||
url = request.route_url('redefinir_mdp', lien=lien)
|
||||
# tester si le champ "motdepasse_oublie" est encore valide
|
||||
membre = get_member_by_mdp_oublie(request, lien)
|
||||
else:
|
||||
user = request.matchdict["user"]
|
||||
lien = request.matchdict["lien"]
|
||||
url = request.route_url('init_mdp', user=user, lien=lien)
|
||||
# tester valeur OK ?
|
||||
if lien == date.today().strftime('%d%m%Y'):
|
||||
# oui, lire le membre
|
||||
membre = get_member_by_id(request, user)
|
||||
else:
|
||||
membre = None
|
||||
|
||||
if membre:
|
||||
if 'form.submitted' in request.params:
|
||||
login = request.params["login"]
|
||||
mdp = request.params["new_password1"]
|
||||
if login == membre.CD_UTI:
|
||||
update_membre_mdp(request, login, mdp)
|
||||
request.session.flash("Votre mot de passe a été modifié avec succès.", 'success')
|
||||
return HTTPFound(location=request.route_url('login'))
|
||||
else:
|
||||
request.session.flash("Identifiant incorrect.", 'danger')
|
||||
return HTTPFound(location=request.route_url('login'))
|
||||
else:
|
||||
request.session.flash("Le lien n'est plus valable.", 'warning')
|
||||
return HTTPFound(location=request.route_url('login'))
|
||||
|
||||
return {
|
||||
'page_title': "Définissez votre mot de passe",
|
||||
'url': url,
|
||||
}
|
||||
|
||||
|
||||
@view_config(route_name='login', renderer='../templates/default/login.pt', permission='view')
|
||||
@view_config(route_name='login_as', renderer='../templates/default/login.pt', permission='view')
|
||||
@forbidden_view_config(renderer='../templates/default/login.pt')
|
||||
def login(request):
|
||||
|
||||
current_route_path = request.current_route_path()
|
||||
login = ''
|
||||
login_url = request.route_url('login')
|
||||
|
||||
referrer = request.url
|
||||
if referrer == login_url:
|
||||
referrer = '/' # never use the login form itself as came_from
|
||||
|
||||
came_from = request.params.get('came_from', referrer)
|
||||
password = ''
|
||||
message = ''
|
||||
if 'form.submitted' in request.params:
|
||||
login = request.params['login']
|
||||
password = request.params['password']
|
||||
record = get_member_by_id(request, login)
|
||||
if record :
|
||||
# mot de passe hash valide ?
|
||||
if record.mdp == to_sha1(password) and record.actif == 1:
|
||||
# get user agent string from request
|
||||
ua_string = request.user_agent
|
||||
user_agent = parse(ua_string)
|
||||
update_last_connection(request, login, request.client_addr + ' - ' + str(user_agent))
|
||||
|
||||
# force le commit car il ne se fait pas automatiquement après l'update
|
||||
transaction.commit()
|
||||
|
||||
headers = remember(request, login)
|
||||
return HTTPFound(location=came_from, headers=headers)
|
||||
|
||||
message = "Email et mot de passe invalides. La connexion a échoué."
|
||||
|
||||
return {
|
||||
'page_title': "",
|
||||
'url': login_url,
|
||||
'came_from': came_from,
|
||||
'login': login,
|
||||
'message': message,
|
||||
}
|
||||
|
||||
|
||||
@view_config(route_name='logout')
|
||||
def logout(request):
|
||||
request.session.invalidate()
|
||||
headers = forget(request)
|
||||
request.session.flash("Vous avez bien été déconnecté.")
|
||||
return HTTPFound(location=request.route_url('login', login=''),
|
||||
headers=headers)
|
||||
|
||||
|
||||
@view_config(route_name='affiche_message', renderer='../templates/default/affiche_message.pt')
|
||||
def affiche_message(request):
|
||||
|
||||
login = request.matchdict['login']
|
||||
messages = request.session.pop_flash()
|
||||
|
||||
return {
|
||||
'page_title': "Demande effectuée",
|
||||
'login': login,
|
||||
'messages' : messages,
|
||||
'url_identification': request.route_url('login_as', login=login)
|
||||
}
|
||||
|
||||
@view_config(route_name='ajax_codepostal')
|
||||
def ajax_codepostal(request):
|
||||
recherche = request.GET['recherche']
|
||||
|
||||
# lire les codes postaux commencant par
|
||||
items = get_codespostaux(request, recherche)
|
||||
liste=[]
|
||||
for row in items:
|
||||
d = row.code_postal + " - " + row.libelle
|
||||
liste.append(d)
|
||||
|
||||
return Response(json.dumps(liste))
|
||||
|
||||
@view_config(route_name='ajax_lookup')
|
||||
def ajax_lookup(request):
|
||||
recherche = request.GET['recherche']
|
||||
societe = recherche[:2] # 1er car.
|
||||
name = recherche[2:]
|
||||
|
||||
# lire les chantiers
|
||||
chantiers = get_chantiers_byName(request, societe, name)
|
||||
liste=[]
|
||||
for row in chantiers:
|
||||
d = "%s | %s-%s"% (row.chantier, societe, row.numero)
|
||||
liste.append(d)
|
||||
|
||||
return Response(json.dumps(liste))
|
||||
|
||||
@view_config(route_name='ajax_client')
|
||||
def ajax_client(request):
|
||||
recherche = request.GET['recherche']
|
||||
societe = recherche[:2] # 1er car.
|
||||
name = recherche[2:]
|
||||
|
||||
# lire les clients
|
||||
clients = get_clients_byName(request, societe, name)
|
||||
liste=[]
|
||||
for row in clients:
|
||||
d = "%s | %s-%s"% (row.NOM, societe, row.CD_CLI)
|
||||
liste.append(d)
|
||||
|
||||
return Response(json.dumps(liste))
|
||||
|
||||
@view_config(route_name='ajax_texte')
|
||||
def ajax_texte(request):
|
||||
groupe = request.GET['groupe']
|
||||
libelle = request.GET['libelle']
|
||||
|
||||
# import pdb;pdb.set_trace()
|
||||
|
||||
# lire les articles commencant par
|
||||
items = get_article(request, 'LIB', groupe, libelle)
|
||||
|
||||
liste=[]
|
||||
for row in items:
|
||||
if groupe == 'TEXTE':
|
||||
d = row.libelle
|
||||
else:
|
||||
d = '%s | %s | %s' % (row.ref, row.libelle, to_euro(row.prixht))
|
||||
liste.append(d)
|
||||
|
||||
return Response(json.dumps(liste))
|
||||
|
||||
@view_config(route_name='ajax_article')
|
||||
def ajax_article(request):
|
||||
groupe = request.GET['groupe']
|
||||
ref = request.GET['ref']
|
||||
|
||||
# lire l'article
|
||||
items = get_article(request, 'REF', groupe, ref)
|
||||
# puis retourne son libellé et son prixht
|
||||
liste=[]
|
||||
d = {}
|
||||
d['ref'] = items.ref
|
||||
d['libelle'] = items.libelle
|
||||
d['prixht'] = "%.2f" % items.prixht
|
||||
liste.append(d)
|
||||
|
||||
return Response(json.dumps(liste))
|
||||
|
||||
|
||||
# -*- coding: utf8 -*-
|
||||
from pyramid.response import Response
|
||||
from pyramid.renderers import render, get_renderer
|
||||
from pyramid.view import (
|
||||
view_config,
|
||||
forbidden_view_config,
|
||||
)
|
||||
from pyramid.security import (
|
||||
remember,
|
||||
forget,
|
||||
)
|
||||
from pyramid.httpexceptions import (
|
||||
HTTPFound,
|
||||
HTTPNotFound,
|
||||
HTTPForbidden,
|
||||
)
|
||||
from datetime import *
|
||||
from sqlalchemy.exc import DBAPIError
|
||||
from ..security import groupfinder
|
||||
from user_agents import parse
|
||||
|
||||
import json
|
||||
import locale
|
||||
import hashlib
|
||||
import imaplib
|
||||
import msal
|
||||
import email
|
||||
|
||||
from ..models.default import *
|
||||
from ..models.agenda import *
|
||||
from ..models.dossier import (
|
||||
get_chantiers_byName,
|
||||
get_clients_byName
|
||||
)
|
||||
|
||||
from ..views.utils import *
|
||||
|
||||
def to_decimal(x):
|
||||
import decimal
|
||||
return decimal.Decimal(str(x))
|
||||
|
||||
def to_euro(x):
|
||||
"""Takes a float and returns 12 345,67 €"""
|
||||
locale.setlocale(locale.LC_ALL,'')
|
||||
return locale.currency(x, True, True)
|
||||
|
||||
def to_euroz(x):
|
||||
"""Takes a float and returns 12 345,67 € if not zero"""
|
||||
if x == 0:
|
||||
return ''
|
||||
else:
|
||||
return to_euro(x)
|
||||
|
||||
def to_decz(x):
|
||||
"""Takes a float and returns a number with 2 dec"""
|
||||
locale.setlocale(locale.LC_ALL,'')
|
||||
if x == 0:
|
||||
return ""
|
||||
else:
|
||||
return locale.format_string('%.2f',x, False)
|
||||
|
||||
def to_sha1(message):
|
||||
return hashlib.sha1(message.encode('utf-8')).hexdigest()
|
||||
|
||||
def to_int(x):
|
||||
try:
|
||||
number = int(x.replace(',', '.'))
|
||||
return number
|
||||
except ValueError:
|
||||
return 0
|
||||
|
||||
def to_percent(x):
|
||||
"""Takes a float and returns a string"""
|
||||
return ("%.2f " % x).replace('.', ',') + "%"
|
||||
|
||||
def generate_auth_string(user, token):
|
||||
return "user=%s\x01auth=Bearer %s\x01\x01" % (user, token)
|
||||
|
||||
def mailbox_connect(request, societe):
|
||||
# connecter au serveur IMAP de la societe
|
||||
if societe == 'PE':
|
||||
mbx_name = 'peinture-dumas@entreprise-dumas.com'
|
||||
elif societe == 'ME':
|
||||
mbx_name = 'menuiserie-dumas@entreprise-dumas.com'
|
||||
elif societe == 'PL':
|
||||
mbx_name = 'versanit-dumas@entreprise-dumas.com'
|
||||
elif societe == 'PO':
|
||||
mbx_name = 'polynet-dumas@entreprise-dumas.com'
|
||||
else:
|
||||
request.session.flash("Cette société est inconnue ou non traitée : %s" % societe, 'danger')
|
||||
return None
|
||||
|
||||
conf = {
|
||||
"authority": "https://login.microsoftonline.com/47f02128-acf0-47a7-a3bf-27a3619d4a4f",
|
||||
"client_id": "4fdf2634-a260-4576-a442-684998f0187b", #AppID
|
||||
"scope": ['https://outlook.office365.com/.default'],
|
||||
"secret": "dZD8Q~GmG_PFi.t_uRiyBEzwLeMZeemQGHUFta~J", #Key-Value
|
||||
"secret-id": "5cae3249-0fc0-43e2-bc0e-d78f41964970", #Key-ID
|
||||
}
|
||||
|
||||
app = msal.ConfidentialClientApplication(conf['client_id'], authority=conf['authority'],
|
||||
client_credential=conf['secret'])
|
||||
|
||||
result = app.acquire_token_silent(conf['scope'], account=None)
|
||||
|
||||
if not result:
|
||||
result = app.acquire_token_for_client(scopes=conf['scope'])
|
||||
|
||||
if "access_token" not in result:
|
||||
request.session.flash("ERREUR de connexion au serveur Exchange : %s" % result.get("error"), 'danger')
|
||||
return None
|
||||
|
||||
try:
|
||||
imap = imaplib.IMAP4_SSL('outlook.office365.com', 993)
|
||||
# se connecter à la mailbox
|
||||
imap.authenticate("XOAUTH2", lambda x: generate_auth_string(mbx_name, result['access_token']).encode("utf-8"))
|
||||
except imaplib.IMAP4.error:
|
||||
request.session.flash("ERREUR connexion au compte %s" % mbx_name, 'danger')
|
||||
return None
|
||||
|
||||
# returne la connection
|
||||
return imap
|
||||
|
||||
@view_config(route_name='home', renderer='../templates/default/home.pt', permission='view')
|
||||
def home(request):
|
||||
logged_in = request.authenticated_userid.upper()
|
||||
# lire la fiche de l'utilisateur
|
||||
member = get_member_by_id(request, logged_in)
|
||||
access = member.access
|
||||
agenda = member.agenda
|
||||
|
||||
datedeb = date.today().strftime("%Y-%m-%d")
|
||||
|
||||
nb_dd_restants = [] # get_dd_restant(request)
|
||||
nb_de_restants = [] # get_de_restant(request)
|
||||
nb_fa_restants = [] # get_fa_restant(request)
|
||||
|
||||
nb_rdv = get_rdv_by_date(request, datedeb, agenda)
|
||||
nb_rdf = get_rdf_null(request)
|
||||
nb_mails = 0
|
||||
|
||||
'''
|
||||
# Récupération le nombre de mails en attente
|
||||
societes = ['PE','ME','PL','PO']
|
||||
|
||||
# critères de recherche des demandes d'interventions de la MAIF
|
||||
search_criteria = ['FROM gestionsinistre@maif.fr SUBJECT "Intervention entreprise partenaire" UNDELETED',
|
||||
'FROM service.sinistres@domus-services.fr UNDELETED']
|
||||
|
||||
|
||||
# lister les demandes par societe
|
||||
for societe in societes:
|
||||
conn = mailbox_connect(request, societe)
|
||||
if conn != None:
|
||||
# select INBOX
|
||||
rv, data = conn.select('INBOX', readonly =True)
|
||||
|
||||
# créer la liste des entêtes des messages à afficher
|
||||
for criteria in search_criteria:
|
||||
rv, data = conn.search(None, criteria)
|
||||
# nb de mails dans la mailbax
|
||||
mail_ids = data[0]
|
||||
nb_mails += len(mail_ids.split())
|
||||
|
||||
# deconnexion du serveur
|
||||
conn.close()
|
||||
conn.logout()
|
||||
'''
|
||||
|
||||
return {
|
||||
'page_title': 'Bienvenue sur %s' % request.host,
|
||||
'project': 'mondumas',
|
||||
'access': access,
|
||||
'logged_in': logged_in,
|
||||
'nb_dd_restants': nb_dd_restants,
|
||||
'nb_de_restants': nb_de_restants,
|
||||
'nb_fa_restants': nb_fa_restants,
|
||||
'nb_rdv': nb_rdv,
|
||||
'nb_rdf': nb_rdf,
|
||||
'nb_mails': nb_mails,
|
||||
}
|
||||
|
||||
@view_config(route_name='envoyer_mdp', renderer='../templates/default/envoyer_mdp.pt')
|
||||
def envoyer_mdp(request):
|
||||
url = request.route_url('envoyer_mdp')
|
||||
message = ''
|
||||
|
||||
if 'form.submitted' in request.params:
|
||||
login = request.params['login']
|
||||
member = get_member_by_id(request, login)
|
||||
if member:
|
||||
# Fabrication du corps du email_passwordMessage
|
||||
lien = update_membre_mdp_oublie(request, login)
|
||||
body = """
|
||||
<p>Bonjour,</p>
|
||||
|
||||
<p>Le lien suivant vous dirigera vers une page où vous pourrez ré-initialiser votre mot de passe d'accès à <b>gestion.entreprise-dumas.com</b>:</p>
|
||||
|
||||
<p>%s</p>
|
||||
|
||||
<p>(Ce lien est valide pendant 168 heures.</p>
|
||||
|
||||
|
||||
<p>
|
||||
Cordialement,<br />
|
||||
gestion.entreprise-dumas.com
|
||||
</p>
|
||||
""" % (request.route_url('redefinir_mdp', lien=lien))
|
||||
# envoyer l'email
|
||||
expediteur = request.registry.settings['mondumas.admin_email']
|
||||
error = send_mail(request, expediteur, [member.email,], "[Ent. Dumas] Demande de ré-initialisation du mot de passe", body)
|
||||
if len(error) > 0:
|
||||
request.session.flash(error, 'warning')
|
||||
else:
|
||||
request.session.flash("Le lien permettant pour redéfinir votre mot de passe vous a été envoyé à l'adresse : %s." % member.email, 'success')
|
||||
return HTTPFound(location=request.route_url('affiche_message', login=login))
|
||||
else:
|
||||
message = "Le mot de passe fourni est incorrect."
|
||||
return {
|
||||
'page_title': "Changer mon mot de passe",
|
||||
'url': url,
|
||||
'message': message,
|
||||
}
|
||||
|
||||
@view_config(route_name='changer_mdp', renderer='../templates/default/changer_mdp.pt', permission='view')
|
||||
def changer_mdp(request):
|
||||
url = request.route_url('changer_mdp')
|
||||
logged_in = request.authenticated_userid
|
||||
message = ''
|
||||
|
||||
member = get_member_by_id(request, logged_in)
|
||||
if member:
|
||||
if 'form.submitted' in request.params:
|
||||
old_password = request.params['old_password']
|
||||
new_password = request.params['new_password1']
|
||||
if member.mdp == to_sha1(old_password):
|
||||
update_membre_mdp(request, logged_in, new_password)
|
||||
request.session.flash("Votre mot de passe a été mis à jour avec succès.")
|
||||
return HTTPFound(location=request.route_url('home'))
|
||||
else:
|
||||
message = "Le mot de passe actuel n'est pas correct."
|
||||
|
||||
return {
|
||||
'page_title': "Changer mon mot de passe",
|
||||
'url': url,
|
||||
'member': member,
|
||||
'message': message,
|
||||
}
|
||||
|
||||
@view_config(route_name='redefinir_mdp', renderer='../templates/default/redefinir_mdp.pt')
|
||||
@view_config(route_name='init_mdp', renderer='../templates/default/redefinir_mdp.pt')
|
||||
def redefinir_mdp(request):
|
||||
if request.matched_route.name == 'redefinir_mdp':
|
||||
lien = request.matchdict["lien"]
|
||||
url = request.route_url('redefinir_mdp', lien=lien)
|
||||
# tester si le champ "motdepasse_oublie" est encore valide
|
||||
membre = get_member_by_mdp_oublie(request, lien)
|
||||
else:
|
||||
user = request.matchdict["user"]
|
||||
lien = request.matchdict["lien"]
|
||||
url = request.route_url('init_mdp', user=user, lien=lien)
|
||||
# tester valeur OK ?
|
||||
if lien == date.today().strftime('%d%m%Y'):
|
||||
# oui, lire le membre
|
||||
membre = get_member_by_id(request, user)
|
||||
else:
|
||||
membre = None
|
||||
|
||||
if membre:
|
||||
if 'form.submitted' in request.params:
|
||||
login = request.params["login"]
|
||||
mdp = request.params["new_password1"]
|
||||
if login == membre.CD_UTI:
|
||||
update_membre_mdp(request, login, mdp)
|
||||
request.session.flash("Votre mot de passe a été modifié avec succès.", 'success')
|
||||
return HTTPFound(location=request.route_url('login'))
|
||||
else:
|
||||
request.session.flash("Identifiant incorrect.", 'danger')
|
||||
return HTTPFound(location=request.route_url('login'))
|
||||
else:
|
||||
request.session.flash("Le lien n'est plus valable.", 'warning')
|
||||
return HTTPFound(location=request.route_url('login'))
|
||||
|
||||
return {
|
||||
'page_title': "Définissez votre mot de passe",
|
||||
'url': url,
|
||||
}
|
||||
|
||||
|
||||
@view_config(route_name='login', renderer='../templates/default/login.pt', permission='view')
|
||||
@view_config(route_name='login_as', renderer='../templates/default/login.pt', permission='view')
|
||||
@forbidden_view_config(renderer='../templates/default/login.pt')
|
||||
def login(request):
|
||||
|
||||
login = ''
|
||||
login_url = request.route_url('login')
|
||||
|
||||
referrer = request.url
|
||||
if referrer == login_url:
|
||||
referrer = '/' # never use the login form itself as came_from
|
||||
|
||||
came_from = request.params.get('came_from', referrer)
|
||||
password = ''
|
||||
message = ''
|
||||
|
||||
if 'form.submitted' in request.params:
|
||||
login = request.params['login']
|
||||
password = request.params['password']
|
||||
record = get_member_by_id(request, login)
|
||||
if record :
|
||||
# mot de passe hash valide ?
|
||||
if record.mdp == to_sha1(password) and record.actif == 1:
|
||||
# get user agent string from request
|
||||
ua_string = request.user_agent
|
||||
user_agent = parse(ua_string)
|
||||
update_last_connection(request, login, request.client_addr + ' - ' + str(user_agent))
|
||||
|
||||
# force le commit car il ne se fait pas automatiquement après l'update
|
||||
transaction.commit()
|
||||
|
||||
headers = remember(request, login)
|
||||
return HTTPFound(location=came_from, headers=headers)
|
||||
|
||||
message = "Email et mot de passe invalides. La connexion a échoué."
|
||||
|
||||
return {
|
||||
'page_title': "",
|
||||
'url': login_url,
|
||||
'came_from': came_from,
|
||||
'login': login,
|
||||
'message': message,
|
||||
}
|
||||
|
||||
|
||||
@view_config(route_name='logout')
|
||||
def logout(request):
|
||||
request.session.invalidate()
|
||||
headers = forget(request)
|
||||
request.session.flash("Vous avez bien été déconnecté.")
|
||||
return HTTPFound(location=request.route_url('login', login=''),
|
||||
headers=headers)
|
||||
|
||||
|
||||
@view_config(route_name='affiche_message', renderer='../templates/default/affiche_message.pt')
|
||||
def affiche_message(request):
|
||||
|
||||
login = request.matchdict['login']
|
||||
messages = request.session.pop_flash()
|
||||
|
||||
return {
|
||||
'page_title': "Demande effectuée",
|
||||
'login': login,
|
||||
'messages' : messages,
|
||||
'url_identification': request.route_url('login_as', login=login)
|
||||
}
|
||||
|
||||
@view_config(route_name='ajax_codepostal')
|
||||
def ajax_codepostal(request):
|
||||
recherche = request.GET['recherche']
|
||||
|
||||
# lire les codes postaux commencant par
|
||||
items = get_codespostaux(request, recherche)
|
||||
liste=[]
|
||||
for row in items:
|
||||
d = row.code_postal + " - " + row.libelle
|
||||
liste.append(d)
|
||||
|
||||
return Response(json.dumps(liste))
|
||||
|
||||
@view_config(route_name='ajax_lookup')
|
||||
def ajax_lookup(request):
|
||||
recherche = request.GET['recherche']
|
||||
societe = recherche[:2] # 1er car.
|
||||
name = recherche[2:]
|
||||
|
||||
# lire les chantiers
|
||||
chantiers = get_chantiers_byName(request, societe, name)
|
||||
liste=[]
|
||||
for row in chantiers:
|
||||
d = "%s | %s-%s"% (row.chantier, societe, row.numero)
|
||||
liste.append(d)
|
||||
|
||||
return Response(json.dumps(liste))
|
||||
|
||||
@view_config(route_name='ajax_client')
|
||||
def ajax_client(request):
|
||||
recherche = request.GET['recherche']
|
||||
societe = recherche[:2] # 1er car.
|
||||
name = recherche[2:]
|
||||
|
||||
# lire les clients
|
||||
clients = get_clients_byName(request, societe, name)
|
||||
liste=[]
|
||||
for row in clients:
|
||||
d = "%s | %s-%s"% (row.NOM, societe, row.CD_CLI)
|
||||
liste.append(d)
|
||||
|
||||
return Response(json.dumps(liste))
|
||||
|
||||
@view_config(route_name='ajax_texte')
|
||||
def ajax_texte(request):
|
||||
groupe = request.GET['groupe']
|
||||
libelle = request.GET['libelle']
|
||||
|
||||
# import pdb;pdb.set_trace()
|
||||
|
||||
# lire les articles commencant par
|
||||
items = get_tarif_ajax(request, 'LIB', groupe, libelle)
|
||||
|
||||
liste=[]
|
||||
for row in items:
|
||||
if groupe == 'TEXTE':
|
||||
d = row.libelle
|
||||
else:
|
||||
d = '%s | %s | %s' % (row.ref, row.libelle, to_euro(row.prixht))
|
||||
liste.append(d)
|
||||
|
||||
return Response(json.dumps(liste))
|
||||
|
||||
@view_config(route_name='ajax_article')
|
||||
def ajax_article(request):
|
||||
groupe = request.GET['groupe']
|
||||
ref = request.GET['ref']
|
||||
|
||||
# lire l'article
|
||||
items = get_tarif_ajax(request, 'REF', groupe, ref)
|
||||
# puis retourne son libellé et son prixht
|
||||
liste=[]
|
||||
d = {}
|
||||
d['ref'] = items.ref
|
||||
d['libelle'] = items.libelle
|
||||
d['prixht'] = "%.2f" % items.prixht
|
||||
d['unite'] = items.unite
|
||||
liste.append(d)
|
||||
return Response(json.dumps(liste))
|
||||
|
||||
@@ -21,35 +21,49 @@ from ..models.devis import *
|
||||
|
||||
@view_config(route_name='devis_list', renderer='../templates/devis/devis_list.pt', permission='view')
|
||||
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 = ''
|
||||
member = get_member_by_id(request, logged_in)
|
||||
societe_defaut = member.societe
|
||||
societe = societe_defaut
|
||||
access_defaut = member.access
|
||||
liste=[]
|
||||
name = ''
|
||||
cb_tous = "non"
|
||||
dossiers=None
|
||||
|
||||
if 'form.submitted' in request.params:
|
||||
name = request.params['name']
|
||||
societe = request.params['societe']
|
||||
|
||||
# lire les devis
|
||||
devis = get_devis_byName(request, societe, name)
|
||||
if len(devis) == 0:
|
||||
message = "Devis non trouvé : %s" % name
|
||||
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)
|
||||
|
||||
|
||||
return {
|
||||
'page_title': "Rechercher un devis",
|
||||
'url': url,
|
||||
'message': message,
|
||||
'devis': devis,
|
||||
'dossiers': dossiers,
|
||||
'societe': societe,
|
||||
'name': name,
|
||||
'nodevis': nodevis,
|
||||
}
|
||||
|
||||
@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')
|
||||
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')
|
||||
@@ -73,6 +98,10 @@ def devis_view(request):
|
||||
logged_in = request.authenticated_userid.upper()
|
||||
url = request.route_url("devis_view", nodevis=nodevis)
|
||||
|
||||
# lire son niveau d'accès
|
||||
member = get_member_by_id(request, logged_in)
|
||||
access = member.access
|
||||
|
||||
type_doc = nodevis[3:5]
|
||||
if type_doc == 'DE':
|
||||
page_title = "Devis no : %s" % (nodevis)
|
||||
@@ -93,6 +122,7 @@ def devis_view(request):
|
||||
bg_color = "bg-%s" % societe
|
||||
|
||||
status = get_status_by_id(request, 'DE')
|
||||
motifs = get_motifs(request)
|
||||
|
||||
if 'form.close' in request.params:
|
||||
status = request.params["status"]
|
||||
@@ -119,7 +149,9 @@ def devis_view(request):
|
||||
'details': details,
|
||||
'bg_color': bg_color,
|
||||
'status': status,
|
||||
'motifs': motifs,
|
||||
'type_doc': type_doc,
|
||||
'access': access,
|
||||
}
|
||||
|
||||
@view_config(route_name='devis_web', renderer='../templates/devis/devis_web.pt', permission='view')
|
||||
@@ -184,7 +216,7 @@ def devis_web(request):
|
||||
os.remove(dest)
|
||||
|
||||
# on est en developpement ou en production
|
||||
if request.registry.settings["mail.username"] == "sasdumas@entreprise-dumas.com":
|
||||
if request.registry.settings["mondumas.admin_email"].find('@entreprise-dumas.com') > 0:
|
||||
origin = 'https://gestion.entreprise-dumas.com/devis_preview/%s' % nodevis
|
||||
pdfkit.from_url(origin, dest, options=options)
|
||||
else:
|
||||
@@ -393,3 +425,79 @@ def devis_lig_mv(request):
|
||||
move_devis_ligne(request, nodevis, int(nolig), move)
|
||||
request.session.flash(u"La ligne de devis a été déplacée avec succès.", 'success')
|
||||
return HTTPFound(location=request.route_url("devis_web", nodevis=nodevis))
|
||||
|
||||
|
||||
@view_config(route_name='devis_selected', permission='view')
|
||||
def devis_selected(request):
|
||||
|
||||
# récupérer les paramètres de l'appel de la view
|
||||
goto = request.matchdict['goto']
|
||||
datePlan = request.matchdict['date']
|
||||
nodevis = request.matchdict['nodevis']
|
||||
|
||||
# fiche dossier
|
||||
devis = get_devis_by_no(request, nodevis)
|
||||
if goto == 'devis_view':
|
||||
return HTTPFound(location=request.route_url('devis_view', nodevis=nodevis))
|
||||
else:
|
||||
return HTTPFound(location=request.route_url('agenda', date=datePlan))
|
||||
|
||||
@view_config(route_name='factures_en_att', renderer='../templates/devis/factures_en_att.pt', permission='view')
|
||||
@view_config(route_name='facture_select', renderer='../templates/devis/factures_en_att.pt', permission='view')
|
||||
def factures_en_att(request):
|
||||
|
||||
if 'facture_select' in request.current_route_path() :
|
||||
# récupérer les paramètres de l'appel de la view
|
||||
datePlan = request.matchdict['date']
|
||||
# sélectionner devis -> goto planning
|
||||
goto_url = '/facture_selected/agenda/%s/' % datePlan
|
||||
url = request.route_url('devis_selected', date=datePlan)
|
||||
else:
|
||||
# recherche devis en att -> goto fiche devis
|
||||
goto_url = '/facture_selected/devis_view/%s/' % date.today().strftime('%Y-%m-%d')
|
||||
url = request.route_url('factures_en_att')
|
||||
|
||||
logged_in = request.authenticated_userid.upper()
|
||||
url = request.route_url('factures_en_att')
|
||||
member = get_member_by_id(request, logged_in)
|
||||
societe_defaut = member.societe
|
||||
societe = societe_defaut
|
||||
access_defaut = member.access
|
||||
liste=[]
|
||||
|
||||
if 'form.submitted' in request.params:
|
||||
societe = request.params['societe']
|
||||
|
||||
list_factures_en_att = get_factures_en_att(request, societe)
|
||||
|
||||
for item in list_factures_en_att:
|
||||
d = ('%s-FA%s' % (societe, item.numero),item.date.strftime('%d-%m-%Y'), item.nomcli, item.chantier, to_euro(item.montant),
|
||||
item.nosin, item.libelle, item.usermaj)
|
||||
liste.append(d)
|
||||
|
||||
order_option = 'desc'
|
||||
|
||||
|
||||
return {
|
||||
'page_title': "Factures en attente de réglement",
|
||||
'url': url,
|
||||
'goto_url': goto_url,
|
||||
'dt_data': json.dumps(liste),
|
||||
'societe': societe,
|
||||
'order_option': order_option,
|
||||
}
|
||||
|
||||
@view_config(route_name='facture_selected', permission='view')
|
||||
def facture_selected(request):
|
||||
|
||||
# récupérer les paramètres de l'appel de la view
|
||||
goto = request.matchdict['goto']
|
||||
datePlan = request.matchdict['date']
|
||||
nofacture = request.matchdict['nofacture']
|
||||
|
||||
# fiche dossier
|
||||
facture = get_facture_by_no(request, nofacture)
|
||||
if goto == 'devis_view':
|
||||
return HTTPFound(location=request.route_url('devis_view', nodevis=nofacture))
|
||||
else:
|
||||
return HTTPFound(location=request.route_url('agenda', date=datePlan))
|
||||
@@ -1,17 +1,10 @@
|
||||
# -*- coding: utf8 -*-
|
||||
from pyramid.response import Response
|
||||
from pyramid.renderers import render, get_renderer
|
||||
from pyramid.view import (
|
||||
view_config,
|
||||
forbidden_view_config,
|
||||
)
|
||||
from pyramid.httpexceptions import (
|
||||
HTTPFound,
|
||||
HTTPNotFound,
|
||||
HTTPForbidden,
|
||||
)
|
||||
from pyramid_mailer import get_mailer
|
||||
from pyramid_mailer.message import Message, Attachment
|
||||
from datetime import *
|
||||
|
||||
|
||||
@@ -20,7 +13,6 @@ import os
|
||||
import io
|
||||
import shutil
|
||||
import pdfkit
|
||||
import imaplib
|
||||
import base64
|
||||
import email
|
||||
|
||||
@@ -56,11 +48,9 @@ def dossier_lookup(request):
|
||||
member = get_member_by_id(request, logged_in)
|
||||
societe_defaut = member.societe
|
||||
societe = societe_defaut
|
||||
access_defaut = member.access
|
||||
liste=[]
|
||||
name = ''
|
||||
cb_tous = "non"
|
||||
|
||||
|
||||
if 'form.submitted' in request.params:
|
||||
name = request.params['name']
|
||||
societe = request.params['societe']
|
||||
@@ -72,7 +62,7 @@ def dossier_lookup(request):
|
||||
# construire la liste
|
||||
for item in chantiers:
|
||||
d = ('%s-%s' % (societe, item.numero),item.date.strftime('%d-%m-%Y'), item.nomcli, item.chantier, to_euro(item.montant),
|
||||
item.nosin, item.status, item.usermaj)
|
||||
item.nosin, item.libelle, item.usermaj)
|
||||
liste.append(d)
|
||||
|
||||
if len(name) == 0 :
|
||||
@@ -127,6 +117,9 @@ def dossier_view(request):
|
||||
bg_color = "bg-%s" % societe
|
||||
|
||||
status = get_status_by_id(request, '')
|
||||
motifs = get_motifs(request)
|
||||
# lire les notes du dossier
|
||||
dem_notes = get_dem_notes(request, nodossier, '0')
|
||||
|
||||
if 'form.close' in request.params:
|
||||
status = request.params["status"]
|
||||
@@ -159,6 +152,9 @@ def dossier_view(request):
|
||||
'bg_color': bg_color,
|
||||
'access': access,
|
||||
'status': status,
|
||||
'motifs': motifs,
|
||||
'motif': '',
|
||||
'dem_notes': dem_notes,
|
||||
}
|
||||
|
||||
@view_config(route_name='dossier_selected', permission='view')
|
||||
@@ -184,6 +180,10 @@ def suivi_edit(request):
|
||||
nolig = request.matchdict['nolig']
|
||||
url = request.route_url("suivi_edit", nodossier=nodossier, nolig=nolig)
|
||||
|
||||
# lire son niveau d'accès
|
||||
member = get_member_by_id(request, logged_in)
|
||||
access = member.access
|
||||
|
||||
message = ''
|
||||
if nolig == '0':
|
||||
# nouveau
|
||||
@@ -230,6 +230,7 @@ def suivi_edit(request):
|
||||
'nolig': nolig,
|
||||
'suivi': suivi,
|
||||
'message': message,
|
||||
'access': access,
|
||||
}
|
||||
|
||||
@view_config(route_name='dossier_edit', renderer='../templates/dossier/dossier_edit.pt', permission='view')
|
||||
@@ -365,7 +366,7 @@ def upload_img(request):
|
||||
societe = rapport.societe
|
||||
nochantier = rapport.NO_ID
|
||||
url = request.route_url("upload_img", norapport=nodossier, origine=origine)
|
||||
url_retour = request.route_url('dossier_view', nodossier=nodossier)
|
||||
url_retour = request.route_url('dossier_view', nodossier=nodossier) + '#tab_attaches'
|
||||
titre = "Gérer les photos du dossier %s" % (nodossier)
|
||||
else:
|
||||
norapport = request.matchdict['norapport']
|
||||
@@ -515,7 +516,7 @@ def rdf_edit(request):
|
||||
if new_values:
|
||||
update_rapport(request, nodossier, date_inter, new_values)
|
||||
request.session.flash(u"Le dossier a été mis à jour avec succès.", 'success')
|
||||
return HTTPFound(location=request.route_url('dossier_view', nodossier=nodossier))
|
||||
return HTTPFound(location=request.route_url('dossier_view', nodossier=nodossier) + '#tab_rdf')
|
||||
|
||||
return {
|
||||
'page_title': "Rapport de RDF : %s du %s" % (nodossier, rapport.date_inter.strftime('%d-%m-%Y')),
|
||||
@@ -585,7 +586,7 @@ def rdf_client(request):
|
||||
if len(nomClient) == 2:
|
||||
update_rapport_client(request, norapport, nomClient[0], nomClient[1])
|
||||
request.session.flash("Le client du rapporta été modifié avec succès.", 'success')
|
||||
return HTTPFound(location=request.route_url('dossier_view', nodossier=nodossier))
|
||||
return HTTPFound(location=request.route_url('dossier_view', nodossier=nodossier) + '#tab_rdf')
|
||||
else:
|
||||
message = "Veuillez saisir un nom de client ou Annuler"
|
||||
|
||||
@@ -598,6 +599,45 @@ def rdf_client(request):
|
||||
'rapport': rapport,
|
||||
}
|
||||
|
||||
@view_config(route_name='rdf_nochantier', renderer='../templates/dossier/rdf_nochantier.pt', permission='view')
|
||||
def rdf_nochantier(request):
|
||||
logged_in = request.authenticated_userid.upper()
|
||||
norapport = request.matchdict['no_id']
|
||||
message = ''
|
||||
|
||||
# lire code accès du user
|
||||
access = get_userAccess(request, logged_in)
|
||||
if access == 0:
|
||||
request.session.flash("Vous n'avez pas les droits nécessaires pour changer de client.", 'danger')
|
||||
return HTTPFound(location=request.route_url('rdf_view', no_id=norapport))
|
||||
|
||||
url = request.route_url('rdf_nochantier', no_id=norapport)
|
||||
|
||||
# lire le rapport
|
||||
rapport = get_rapport_by_no_id(request, norapport)
|
||||
nodossier = 'PL-' + str(rapport.nochantier)
|
||||
|
||||
if 'form.submitted' in request.params:
|
||||
new_nochantier = request.params['new_nochantier']
|
||||
if len(new_nochantier) == 6:
|
||||
retour = update_rapport_nochantier(request, norapport, new_nochantier)
|
||||
if retour == "OK":
|
||||
request.session.flash("Le numéro du dossier été modifié avec succès.", 'success')
|
||||
return HTTPFound(location=request.route_url('rdf_view', no_id=norapport))
|
||||
else:
|
||||
message = "Le numéro du dossier n'existe pas : %s" % new_nochantier
|
||||
else:
|
||||
message = "Un numero de dossier doit avoir 6 chiffres"
|
||||
|
||||
return {
|
||||
'page_title': "Changer le no du dossier du rapport n° %s" % norapport,
|
||||
'url': url,
|
||||
'message': message,
|
||||
'access': access,
|
||||
'norapport': norapport,
|
||||
'rapport': rapport,
|
||||
}
|
||||
|
||||
@view_config(route_name='rdf_view', renderer='../templates/dossier/rdf_view.pt', permission='view')
|
||||
def rdf_view(request):
|
||||
logged_in = request.authenticated_userid.upper()
|
||||
@@ -651,7 +691,7 @@ def rdf_view(request):
|
||||
os.remove(dest)
|
||||
|
||||
# developpement ou production
|
||||
if request.registry.settings["mail.username"].find('@entreprise-dumas.com') > 0:
|
||||
if request.registry.settings["mondumas.admin_email"].find('@entreprise-dumas.com') > 0:
|
||||
origin = 'https://gestion.entreprise-dumas.com/rdf_rapport/%s' % norapport
|
||||
pdfkit.from_url(origin, dest, options=options)
|
||||
else:
|
||||
@@ -666,12 +706,12 @@ def rdf_view(request):
|
||||
update_rapport(request, nodossier, rapport.date_inter.strftime('%Y-%m-%d'), new_values)
|
||||
|
||||
request.session.flash(u"Le rapport a été généré avec succès.", 'success')
|
||||
return HTTPFound(location=request.route_url('dossier_view', nodossier=nodossier))
|
||||
return HTTPFound(location=request.route_url('dossier_view', nodossier=nodossier) + '#tab_rdf')
|
||||
|
||||
if 'form.validate' in request.params:
|
||||
update_rapport_validate(request, norapport)
|
||||
request.session.flash(u"Le rapporta été validé avec succès.", 'success')
|
||||
return HTTPFound(location=request.route_url('dossier_view', nodossier=nodossier))
|
||||
return HTTPFound(location=request.route_url('dossier_view', nodossier=nodossier) + '#tab_rdf')
|
||||
|
||||
if 'form.delete' in request.params:
|
||||
# le rapport a-t-il des photos ?
|
||||
@@ -681,7 +721,7 @@ def rdf_view(request):
|
||||
|
||||
delete_rapport(request, norapport)
|
||||
request.session.flash(u"Le rapport a été supprimé avec succès.", 'success')
|
||||
return HTTPFound(location=request.route_url('dossier_view', nodossier=nodossier))
|
||||
return HTTPFound(location=request.route_url('dossier_view', nodossier=nodossier) + '#tab_rdf')
|
||||
|
||||
return {
|
||||
'page_title': "Rapport no %s du %s" % (norapport, rapport.date_inter.strftime('%d-%m-%Y')),
|
||||
@@ -830,7 +870,7 @@ def rdf_bill(request):
|
||||
update_rapport_facture(request, norapport)
|
||||
|
||||
request.session.flash("Le rapport a été généré avec succès.", 'success')
|
||||
return HTTPFound(location=request.route_url('dossier_view', nodossier=nodossier))
|
||||
return HTTPFound(location=request.route_url('dossier_view', nodossier=nodossier) + '#tab_rdf')
|
||||
|
||||
return {
|
||||
'page_title': "Générer une facture pour le rapport du %s" % (rapport.date_inter.strftime('%d-%m-%Y')),
|
||||
@@ -849,13 +889,16 @@ def rdf_bill(request):
|
||||
def demandes(request):
|
||||
|
||||
def demandes_lister(societe, search_criteria):
|
||||
# créer la liste des entêtes des messages à afficher
|
||||
liste = []
|
||||
# connecter au serveur de mail
|
||||
conn = mailbox_connect(request, societe)
|
||||
if conn == None:
|
||||
return liste
|
||||
|
||||
# select INBOX
|
||||
rv, data = conn.select('INBOX', readonly =True)
|
||||
|
||||
# créer la liste des entêtes des messages à afficher
|
||||
liste = []
|
||||
for criteria in search_criteria:
|
||||
rv, data = conn.search(None, criteria)
|
||||
if rv != 'OK':
|
||||
@@ -904,21 +947,20 @@ def demandes(request):
|
||||
# critères de recherche des demandes d'interventions de la MAIF
|
||||
# search_criteria = ['FROM service.sinistres@domus-services.fr SUBJECT "Ordre de mission DOMUS - Dossier" UNDELETED'] "
|
||||
search_criteria = ['FROM gestionsinistre@maif.fr SUBJECT "Intervention entreprise partenaire"',
|
||||
'FROM service.sinistres@domus-services.fr UNDELETED']
|
||||
# 'FROM service.sinistres@domus-services.fr SUBJECT "Ordre de mission DOMUS - Dossier" UNDELETED']
|
||||
'FROM service.sinistres@domus-services.fr SUBJECT "Ordre de mission DOMUS" UNDELETED']
|
||||
|
||||
emails=[]
|
||||
# lister les demandes par societe
|
||||
for societe in societes:
|
||||
emails = emails + demandes_lister(societe, search_criteria)
|
||||
|
||||
# messages lus
|
||||
msglus = bool(emails)
|
||||
dossiers_traites = get_dossiers_importes(request)
|
||||
|
||||
return {
|
||||
'page_title': "Liste des emails de demandes d'intervention",
|
||||
'url': url,
|
||||
'emails': emails,
|
||||
'dossiers_traites': dossiers_traites,
|
||||
}
|
||||
|
||||
@view_config(route_name='demandes_dl', permission='view')
|
||||
@@ -932,24 +974,21 @@ def demandes_dl(request):
|
||||
|
||||
if 'maif.fr' in mbx_search:
|
||||
# extraire les infos de la demmande MAIF
|
||||
dem_info = get_pdf_infos1(extracted_file)
|
||||
dem_info = get_pdf_infos_maif(extracted_file)
|
||||
if societe == 'PE':
|
||||
cd_cli = 2813
|
||||
elif societe == 'ME':
|
||||
cd_cli = 3428
|
||||
cd_cli = 589
|
||||
elif societe == 'PO':
|
||||
cd_cli = 31
|
||||
else:
|
||||
# VERSANIT
|
||||
cd_cli = 1743
|
||||
elif 'domus-services.fr' in mbx_search:
|
||||
# extraire les infos de la demmande DOMUS
|
||||
dem_info = get_pdf_infos2(extracted_file)
|
||||
if societe == 'PE':
|
||||
cd_cli = 8991
|
||||
elif societe == 'ME':
|
||||
cd_cli = 5276
|
||||
else:
|
||||
# VERSANIT
|
||||
cd_cli = 3209
|
||||
dem_info = get_pdf_infos_domus(extracted_file)
|
||||
# toutes les demandes sont pour la société PE
|
||||
cd_cli = 8991
|
||||
|
||||
# extraction OK ? oui, créer une dem_devis et récupèrer son no_id
|
||||
traite = 0
|
||||
@@ -983,7 +1022,7 @@ def demandes_dl(request):
|
||||
|
||||
def generer_annul_maif(request, societe, extracted_file, temp_file_path):
|
||||
# extraire les infos de la demmande MAIF
|
||||
dem_info = get_pdf_infos1(extracted_file)
|
||||
dem_info = get_pdf_infos_maif(extracted_file)
|
||||
# extraction OK ? oui, rechercher la dem_devis concerné
|
||||
|
||||
traite = 0
|
||||
@@ -1029,12 +1068,16 @@ def demandes_dl(request):
|
||||
email_from = request.matchdict['email_from']
|
||||
email_uid = request.matchdict['email_uid']
|
||||
logged_in = request.authenticated_userid.upper()
|
||||
|
||||
message = ''
|
||||
|
||||
if logged_in == 'TST':
|
||||
request.session.flash("Cette fonction est désactivée dans cette version de test", 'warning')
|
||||
return HTTPFound(request.route_url('demandes'))
|
||||
|
||||
if 'maif' in email_from:
|
||||
search_criteria = 'FROM gestionsinistre@maif.fr SUBJECT "Intervention entreprise partenaire" UNDELETED'
|
||||
else:
|
||||
search_criteria = 'FROM service.sinistres@domus-services.fr SUBJECT "Ordre de mission DOMUS - Dossier" UNDELETED'
|
||||
search_criteria = 'FROM ne-pas-repondre@domus-services.fr SUBJECT "Ordre de mission DOMUS" UNDELETED'
|
||||
|
||||
# connecter au serveur de mail
|
||||
conn = mailbox_connect(request, societe)
|
||||
@@ -1046,13 +1089,13 @@ def demandes_dl(request):
|
||||
rv, data = conn.search(None, search_criteria)
|
||||
if rv != 'OK':
|
||||
request.session.flash("ERREUR de lecture de la boîte de réception", 'danger')
|
||||
return HTTPFound(location=request.route_url('messages'))
|
||||
return HTTPFound(location=request.route_url('demandes'))
|
||||
|
||||
# lire le message avec UID
|
||||
rv, msg_data = conn.fetch(email_uid, '(RFC822)')
|
||||
if rv != 'OK':
|
||||
request.session.flash("ERREUR de lecture du message %S-%s" % (email_from, email_uid), 'danger')
|
||||
return HTTPFound(location=request.route_url('messages'))
|
||||
return HTTPFound(location=request.route_url('demandes'))
|
||||
|
||||
email_message = email.message_from_bytes(msg_data[0][1])
|
||||
|
||||
@@ -1075,18 +1118,18 @@ def demandes_dl(request):
|
||||
# mission annulée ?
|
||||
if 'Objet : ANNULATION MISSION' in texte :
|
||||
# genere ANNULATION mission MAIF
|
||||
#import pdb;pdb.set_trace()
|
||||
n = generer_annul_maif(request, societe, extracted_file, temp_file_path)
|
||||
if n > 0:
|
||||
# déplacer le message dans la poubelle
|
||||
# import pdb;pdb.set_trace()
|
||||
conn.store(email_uid, '+FLAGS', '\\Deleted')
|
||||
elif 'ANNULATION ORDRE DE MISSION' in texte:
|
||||
# genere ANNULATION mission DOMUS
|
||||
nosin = texte.split('\n')[3] # 4ème ligne de texte
|
||||
#import pdb;pdb.set_trace()
|
||||
n = generer_annul_domus(request, societe, nosin, temp_file_path)
|
||||
if n > 0:
|
||||
# déplacer le message dans la poubelle
|
||||
# import pdb;pdb.set_trace()
|
||||
conn.store(email_uid, '+FLAGS', '\\Deleted')
|
||||
else:
|
||||
#import pdb;pdb.set_trace()
|
||||
@@ -1094,6 +1137,7 @@ def demandes_dl(request):
|
||||
n = generer_mission(request, societe, email_from, extracted_file, temp_file_path)
|
||||
if n > 0:
|
||||
# déplacer le message dans la poubelle
|
||||
# import pdb;pdb.set_trace()
|
||||
conn.store(email_uid, '+FLAGS', '\\Deleted')
|
||||
|
||||
conn.expunge()
|
||||
@@ -1206,13 +1250,13 @@ def pdf_convert_to_txt(path):
|
||||
|
||||
return extracted_text, extracted_file
|
||||
|
||||
def get_pdf_infos1(extracted_file):
|
||||
def get_pdf_infos_maif(extracted_file):
|
||||
# à partir du fichier texte du pdf
|
||||
# parcourir les lignes pour retrouver les infos utiles
|
||||
with open(extracted_file, encoding="utf-8") as fp:
|
||||
cnt = 1
|
||||
line = fp.readline()
|
||||
# première ligne doit être "MAIF"
|
||||
# première ligne doit contenir "MAIF" ou "FILIA-MAIF"
|
||||
if line[:-1] not in ['MAIF', 'FILIA-MAIF']:
|
||||
return {'c_nom': ''}
|
||||
|
||||
@@ -1224,7 +1268,8 @@ def get_pdf_infos1(extracted_file):
|
||||
if line.find('Nos références') == 0:
|
||||
line = fp.readline()
|
||||
line = fp.readline()
|
||||
no_sinistre = line[:-1]
|
||||
# ne prendre que les 11 premiers car. du no sinistre MAIF
|
||||
no_sinistre = line[:11]
|
||||
if line.find('Bénéficiaire des travaux :') == 0:
|
||||
elt = line[:-1].split(' :')
|
||||
if len(elt) == 1:
|
||||
@@ -1288,7 +1333,7 @@ def get_pdf_infos1(extracted_file):
|
||||
'no_sinistre': no_sinistre,
|
||||
}
|
||||
|
||||
def get_pdf_infos2(extracted_file):
|
||||
def get_pdf_infos_domus(extracted_file):
|
||||
# à partir du fichier texte du pdf de DOMUS
|
||||
# parcourir les lignes pour retrouver les infos utiles
|
||||
with open(extracted_file, encoding="utf-8") as fp:
|
||||
@@ -1349,7 +1394,7 @@ def get_pdf_infos2(extracted_file):
|
||||
'no_sinistre': no_sinistre,
|
||||
}
|
||||
|
||||
def get_pdf_infos3(extracted_file):
|
||||
def get_pdf_infos_axa(extracted_file):
|
||||
# à partir du fichier texte du pdf de AXA
|
||||
# parcourir les lignes pour retrouver les infos utiles
|
||||
with open(extracted_file, encoding="utf-8") as fp:
|
||||
@@ -1357,19 +1402,19 @@ def get_pdf_infos3(extracted_file):
|
||||
cnt = 1
|
||||
line = fp.readline()
|
||||
# première ligne doit être :
|
||||
if line[:-1] != 'Assurance et Banque':
|
||||
fp.close()
|
||||
return {'c_nom': ''}
|
||||
#if line[:-1] != 'Assurance et Banque':
|
||||
# fp.close()
|
||||
# return {'c_nom': ''}
|
||||
|
||||
c_nom = ''
|
||||
c_telp = ''
|
||||
c_email = ''
|
||||
while line:
|
||||
if line[:-1] == 'LibellØ':
|
||||
# import pdb;pdb.set_trace()
|
||||
line = fp.readline()
|
||||
if 'PEINTURE' in line or 'PAPIER PEINT' in line or 'CARRELAGE' in line or 'CERAMIQUE' in line:
|
||||
# déterminer la société
|
||||
if '@entreprise-dumas.com' in line :
|
||||
if 'peinture' in line :
|
||||
societe = 'PE'
|
||||
elif 'MENUISERIE' in line or 'FERMETURE' in line or 'PARQUET' in line:
|
||||
elif 'menuiserie' in line :
|
||||
societe = 'ME'
|
||||
else:
|
||||
societe = ''
|
||||
@@ -1429,6 +1474,176 @@ def get_pdf_infos3(extracted_file):
|
||||
'no_police': no_police,
|
||||
'no_sinistre': no_sinistre,
|
||||
'societe': societe,
|
||||
'assureur': 'AXA'
|
||||
}
|
||||
|
||||
def get_pdf_infos_sinapps_AXA(extracted_file):
|
||||
# à partir du fichier texte du pdf de SINAPPS
|
||||
# parcourir les lignes pour retrouver les infos utiles
|
||||
with open(extracted_file, encoding="utf-8") as fp:
|
||||
|
||||
line = fp.readline()
|
||||
# première ligne doit être :
|
||||
if line[:-1] != 'Mission':
|
||||
fp.close()
|
||||
return {'c_nom': ''}
|
||||
else:
|
||||
line = fp.readline()
|
||||
if 'AVANSSUR' in line:
|
||||
assureur = 'AVANSSUR'
|
||||
else:
|
||||
assureur = 'AXA'
|
||||
|
||||
c_nom = ''
|
||||
c_telp = ''
|
||||
c_email = ''
|
||||
while line:
|
||||
# déterminer la société
|
||||
if 'Prestataire' in line :
|
||||
line = fp.readline()
|
||||
line = fp.readline()
|
||||
line = fp.readline()
|
||||
line = fp.readline()
|
||||
# import pdb;pdb.set_trace()
|
||||
if line[:-1] == 'DUMAS' :
|
||||
societe = 'PE'
|
||||
elif line[:-1] == 'MENUISERIE DUMAS' :
|
||||
societe = 'ME'
|
||||
else :
|
||||
societe = ''
|
||||
|
||||
if line[:-1] == 'Numéro du contrat':
|
||||
line = fp.readline()
|
||||
no_police = line[:-1].lstrip("0")
|
||||
if 'Référence sinistre : ' in line:
|
||||
# import pdb;pdb.set_trace()
|
||||
no_sinistre = line[:-1].split(':')[1]
|
||||
no_sinistre = no_sinistre.strip().lstrip("0")
|
||||
if line[:-1] == 'Assuré/client':
|
||||
# import pdb;pdb.set_trace()
|
||||
line = fp.readline()
|
||||
c_nom = line[:-1].strip()
|
||||
line = fp.readline()
|
||||
c_email = line[:-1].strip()
|
||||
line = fp.readline()
|
||||
# tél = les 10 derniers caratères
|
||||
c_telp = line[-12:-1].strip()
|
||||
line = fp.readline()
|
||||
c_adr = line[:-1].strip()
|
||||
line = fp.readline()
|
||||
c_adr2 = line[:-1].strip()
|
||||
line = fp.readline()
|
||||
c_adr3 = line[:-1]
|
||||
# début 3ème ligne adr est un code postal ?
|
||||
if to_int(c_adr3[0:5]) > 0 :
|
||||
# oui, mémoriser le code postal et la ville
|
||||
c_cp = c_adr3[0:5]
|
||||
c_ville = c_adr3[6:].strip()
|
||||
else:
|
||||
# non, le code postal et la ville se trouvent dans la 2è ligne
|
||||
c_cp = c_adr2[0:5]
|
||||
c_ville = c_adr2[6:]
|
||||
c_adr2 = ''
|
||||
# lire ligne suivante
|
||||
line = fp.readline()
|
||||
|
||||
fp.close()
|
||||
# import pdb;pdb.set_trace()
|
||||
|
||||
return {'c_nom': c_nom,
|
||||
'c_adr': c_adr,
|
||||
'c_adr2': c_adr2,
|
||||
'c_cp': c_cp,
|
||||
'c_ville': c_ville,
|
||||
'c_telp': c_telp,
|
||||
'c_email': c_email,
|
||||
'no_police': no_police,
|
||||
'no_sinistre': no_sinistre,
|
||||
'societe': societe,
|
||||
'assureur': assureur,
|
||||
}
|
||||
|
||||
def get_pdf_infos_sinapps_MAIF(extracted_file):
|
||||
# à partir du fichier texte du pdf de SINAPPS
|
||||
# parcourir les lignes pour retrouver les infos utiles
|
||||
with open(extracted_file, encoding="utf-8") as fp:
|
||||
|
||||
line = fp.readline()
|
||||
# première ligne doit être :
|
||||
if line[:-1] != 'Mission':
|
||||
fp.close()
|
||||
return {'c_nom': ''}
|
||||
|
||||
c_nom = ''
|
||||
c_telp = ''
|
||||
c_email = ''
|
||||
no_police = ''
|
||||
while line:
|
||||
# déterminer la société
|
||||
if 'Prestataire' in line :
|
||||
# import pdb;pdb.set_trace()
|
||||
line = fp.readline()
|
||||
if 'VER SANIT' in line :
|
||||
societe = 'PL'
|
||||
else:
|
||||
line = fp.readline()
|
||||
line = fp.readline()
|
||||
line = fp.readline()
|
||||
if 'DUMAS' in line :
|
||||
societe = 'PE'
|
||||
elif 'MENUISERIE DUMAS' in line :
|
||||
societe = 'ME'
|
||||
elif 'VER SANIT' in line :
|
||||
societe = 'PL'
|
||||
else:
|
||||
societe = ''
|
||||
|
||||
if 'Référence sinistre : ' in line:
|
||||
# import pdb;pdb.set_trace()
|
||||
no_sinistre = line[:-1].split(':')[1]
|
||||
no_sinistre = no_sinistre.strip().lstrip("0")
|
||||
if line[:-1] == 'Assuré/client':
|
||||
# import pdb;pdb.set_trace()
|
||||
line = fp.readline()
|
||||
c_nom = line[:-1].strip()
|
||||
line = fp.readline()
|
||||
c_email = line[:-1].strip()
|
||||
line = fp.readline()
|
||||
# tél = les 10 derniers caratères
|
||||
c_telp = line[-12:-1].strip()
|
||||
line = fp.readline()
|
||||
c_adr = line[:-1].strip()
|
||||
line = fp.readline()
|
||||
c_adr2 = line[:-1].strip()
|
||||
line = fp.readline()
|
||||
c_adr3 = line[:-1]
|
||||
# début 3ème ligne adr est un code postal ?
|
||||
if to_int(c_adr3[0:5]) > 0 :
|
||||
# oui, mémoriser le code postal et la ville
|
||||
c_cp = c_adr3[0:5]
|
||||
c_ville = c_adr3[6:].strip()
|
||||
else:
|
||||
# non, le code postal et la ville se trouvent dans la 2è ligne
|
||||
c_cp = c_adr2[0:5]
|
||||
c_ville = c_adr2[6:]
|
||||
c_adr2 = ''
|
||||
# lire ligne suivante
|
||||
line = fp.readline()
|
||||
|
||||
fp.close()
|
||||
# import pdb;pdb.set_trace()
|
||||
|
||||
return {'c_nom': c_nom,
|
||||
'c_adr': c_adr,
|
||||
'c_adr2': c_adr2,
|
||||
'c_cp': c_cp[:5],
|
||||
'c_ville': c_ville,
|
||||
'c_telp': c_telp,
|
||||
'c_email': c_email,
|
||||
'no_police': no_police,
|
||||
'no_sinistre': no_sinistre,
|
||||
'societe': societe,
|
||||
'assureur': 'MAIF'
|
||||
}
|
||||
|
||||
def resize_photos(image_file):
|
||||
@@ -1436,12 +1651,9 @@ def resize_photos(image_file):
|
||||
img_org = Image.open(image_file)
|
||||
# get the size of the original image
|
||||
width_org, height_org = img_org.size
|
||||
# set the resizing factor so the aspect ratio can be retained
|
||||
# factor > 1.0 increases size
|
||||
# factor < 1.0 decreases size
|
||||
factor = 0.50
|
||||
width = int(width_org * factor)
|
||||
height = int(height_org * factor)
|
||||
# set the max width
|
||||
width = 1366
|
||||
height = int(height_org / width_org * width)
|
||||
# best down-sizing filter
|
||||
img_anti = img_org.resize((width, height), Image.ANTIALIAS)
|
||||
# split image filename into name and extension
|
||||
@@ -1466,32 +1678,61 @@ def upload_om(request):
|
||||
UPLOAD d'un ordre de mission en PDF
|
||||
|
||||
"""
|
||||
def generer_mission(request, extracted_file, temp_file_path):
|
||||
|
||||
# extraire les infos de la demmande AXA
|
||||
dem_info = get_pdf_infos3(extracted_file)
|
||||
def generer_mission(request, dem_info, temp_file_path):
|
||||
# import pdb;pdb.set_trace()
|
||||
societe = dem_info['societe']
|
||||
if societe == 'PE':
|
||||
cd_cli = 15207
|
||||
elif societe == 'ME':
|
||||
cd_cli = 1190
|
||||
else:
|
||||
|
||||
if societe == '':
|
||||
return "Descriptif de travaux non prevu par le programme. Prévenir M. CAO."
|
||||
|
||||
if dem_info['assureur'] == 'AXA':
|
||||
if societe == 'PE':
|
||||
cd_cli = 9150
|
||||
else :
|
||||
# menuiserie
|
||||
cd_cli = 5858
|
||||
elif dem_info['assureur'] == 'AVANSSUR':
|
||||
if societe == 'PE':
|
||||
cd_cli = 10149
|
||||
else :
|
||||
# menuiserie
|
||||
cd_cli = 1929
|
||||
else:
|
||||
# assureur = MAIF
|
||||
if societe == 'PE':
|
||||
cd_cli = 2813
|
||||
elif societe == 'ME':
|
||||
# menuiserie
|
||||
cd_cli = 589
|
||||
else:
|
||||
cd_cli = 1743
|
||||
|
||||
# extraction OK ? oui, créer une dem_devis et récupèrer son no_id
|
||||
message = ''
|
||||
if dem_info['c_nom'] != '':
|
||||
# dem_devis n'existe pas, creer nouveau dossier
|
||||
nochantier = insert_dossier(request, societe, cd_cli, dem_info['c_nom'], dem_info['c_adr'], dem_info['c_adr2'], dem_info['c_cp'], \
|
||||
dem_info['c_ville'], dem_info['c_telp'], dem_info['c_email'], dem_info['no_sinistre'], dem_info['no_police'], '', '')
|
||||
nodossier = "%s-%s" % (societe, nochantier)
|
||||
# log de nuit
|
||||
print('--> CREER DOSSIER sinistre %s <--' % nodossier)
|
||||
|
||||
# récupère le nom du fichier et ajouter le no de dossier
|
||||
filename = os.path.basename(temp_file_path)
|
||||
filename = '%s-DD%s-%s' % (societe, nochantier, filename)
|
||||
tempFile2Dossier(request, societe, nochantier, '0', 'CLT', temp_file_path, filename, 'EMAIL')
|
||||
# oui, rechercher la dem_devis concerné par le no de sinistre
|
||||
nosin = dem_info['no_sinistre']
|
||||
dem_devis = get_dossier_by_sinistre(request,societe, nosin)
|
||||
if dem_devis:
|
||||
# dem_devis existe, ajouter le PDF dans ce dossier
|
||||
nochantier = dem_devis.NO_ID
|
||||
nodossier = "%s-%s" % (societe, nochantier)
|
||||
# insérer une ligne de suivi ANNULATION
|
||||
insert_suivi(request, nodossier, '!!MISSION CONFIRMEE ou MODIFIEE PAR la MAIF')
|
||||
# log de nuit
|
||||
print('--> MODIFIER DOSSIER sinistre %s <--' % nodossier)
|
||||
else:
|
||||
# dem_devis n'existe pas, creer nouveau dossier
|
||||
nochantier = insert_dossier(request, societe, cd_cli, dem_info['c_nom'], dem_info['c_adr'], dem_info['c_adr2'], dem_info['c_cp'], \
|
||||
dem_info['c_ville'], dem_info['c_telp'], dem_info['c_email'], dem_info['no_sinistre'], dem_info['no_police'], '', '')
|
||||
nodossier = "%s-%s" % (societe, nochantier)
|
||||
# log de nuit
|
||||
print('--> CREER DOSSIER sinistre %s <--' % nodossier)
|
||||
|
||||
# récupère le nom du fichier et ajouter le no de dossier
|
||||
filename = os.path.basename(temp_file_path)
|
||||
filename = '%s-DD%s-%s' % (societe, nochantier, filename)
|
||||
tempFile2Dossier(request, societe, nochantier, '0', 'CLT', temp_file_path, filename, 'EMAIL')
|
||||
|
||||
return message
|
||||
|
||||
@@ -1522,22 +1763,34 @@ def upload_om(request):
|
||||
|
||||
# importer le pdf
|
||||
if 'form.submitted' in request.params:
|
||||
# origine du PDF = AXA ?
|
||||
if 'AXA France GESTION SINISTRES' in extracted_text :
|
||||
# PDF = ordre de mission AXA ?
|
||||
if 'bon de commande pour les travaux' in extracted_text :
|
||||
# genere le dossier d'après le fichier PDF
|
||||
message = generer_mission(request, extracted_file, temp_file)
|
||||
if message == '':
|
||||
request.session.flash("Le fichier PDF a été importé avec succès.", 'success')
|
||||
return HTTPFound(location=request.route_url('dossier_lookup'))
|
||||
else:
|
||||
message = "ERREUR : L'importation de ce document AXA n'est pas prévue."
|
||||
# import pdb;pdb.set_trace()
|
||||
# Déterminer l'origine du PDF
|
||||
if 'Votre conseiller AXA' in extracted_text and 'bon de commande pour les travaux' in extracted_text:
|
||||
# PDF = ordre de mission AXA ?
|
||||
# extraire les infos de la demmande AXA
|
||||
dem_info = get_pdf_infos_axa(extracted_file)
|
||||
elif 'Envoyé par MAIF le ' in extracted_text :
|
||||
# PDF envoyé par sur SINAPPS
|
||||
# extraire les infos de la mission
|
||||
dem_info = get_pdf_infos_sinapps_MAIF(extracted_file)
|
||||
elif 'Envoyé par AXA FRANCE IRD le' in extracted_text or 'Envoyé par AVANSSUR - DIRECT ASSURANCE le' in extracted_text:
|
||||
# PDF envoyé par AXA ou AVANSSUR sur SINAPPS
|
||||
# extraire les infos de la mission
|
||||
dem_info = get_pdf_infos_sinapps_AXA(extracted_file)
|
||||
else:
|
||||
message = "ERREUR : L'importation de ce type de document n'est pas prévue."
|
||||
|
||||
message = "ERREUR : Ce document n'est pas une demande de prestations AXA ou MAIF."
|
||||
# genere le dossier d'après le fichier PDF
|
||||
|
||||
if message == '':
|
||||
message = generer_mission(request, dem_info, temp_file)
|
||||
if message :
|
||||
request.session.flash(message, 'danger')
|
||||
else:
|
||||
request.session.flash("Le fichier PDF de %s a été importé dans la societe %s avec succès." % (dem_info['assureur'], dem_info['societe']), 'success')
|
||||
return HTTPFound(location=request.route_url('dossier_lookup'))
|
||||
|
||||
return {
|
||||
'page_title': 'Importer un ordre de mission AXA',
|
||||
'page_title': 'Importer un ordre de mission AXA ou MAIF',
|
||||
'url': url,
|
||||
'message': message,
|
||||
'html_text': html_text,
|
||||
@@ -1546,38 +1799,130 @@ def upload_om(request):
|
||||
@view_config(route_name='dem_devis', renderer='../templates/dossier/dem_devis.pt', permission='view')
|
||||
def dem_devis(request):
|
||||
logged_in = request.authenticated_userid.upper()
|
||||
goto_url = '/dossier_selected/dossier_view/%s/' % date.today().strftime('%Y-%m-%d')
|
||||
url = request.route_url('dem_devis')
|
||||
member = get_member_by_id(request, logged_in)
|
||||
societe_defaut = member.societe
|
||||
societe = societe_defaut
|
||||
access_defaut = member.access
|
||||
liste=[]
|
||||
|
||||
dossiers_traites = get_dossiers_traites(request)
|
||||
if 'form.submitted' in request.params:
|
||||
societe = request.params['societe']
|
||||
|
||||
dossiers_traites = get_dossiers_traites(request, societe)
|
||||
|
||||
for item in dossiers_traites:
|
||||
d = ('%s-%s' % (societe, item.numero),item.date.strftime('%d-%m-%Y'), item.nomcli, item.chantier, to_euro(item.montant),
|
||||
item.nosin, item.libelle, item.usermaj)
|
||||
liste.append(d)
|
||||
|
||||
order_option = 'desc'
|
||||
|
||||
return {
|
||||
'page_title': 'Dossiers générés à traiter',
|
||||
'page_title': 'Dossiers avec statut: "A TRAITER"',
|
||||
'url': url,
|
||||
'dossiers_traites':dossiers_traites,
|
||||
'goto_url': goto_url,
|
||||
'dt_data': json.dumps(liste),
|
||||
'societe': societe,
|
||||
'order_option': order_option,
|
||||
}
|
||||
|
||||
@view_config(route_name='devis_en_att', renderer='../templates/dossier/devis_en_att.pt', permission='view')
|
||||
def dem_devis(request):
|
||||
@view_config(route_name='note_edit', renderer='../templates/dossier/note_edit.pt', permission='view')
|
||||
def note_edit(request):
|
||||
logged_in = request.authenticated_userid.upper()
|
||||
url = request.route_url('devis_en_att')
|
||||
nodossier = request.matchdict['nodossier']
|
||||
noligne = request.matchdict['noligne']
|
||||
url = request.route_url('note_edit', nodossier=nodossier, noligne=noligne)
|
||||
|
||||
list_devis_en_att = get_devis_en_att(request)
|
||||
message = ""
|
||||
if noligne == '0':
|
||||
page_title = nodossier + ' : Nouvelle note'
|
||||
note = {}
|
||||
note['notes'] = ''
|
||||
note['usermaj'] = logged_in
|
||||
note['modif_le'] = date.today()
|
||||
else:
|
||||
note = get_dem_notes(request, nodossier, noligne)
|
||||
page_title = nodossier + " : " + note.libelle
|
||||
|
||||
if 'form.submitted' in request.params:
|
||||
notes = request.params["notes"]
|
||||
|
||||
update_dem_note(request, nodossier, noligne, notes, logged_in)
|
||||
request.session.flash("La note a été modifiée avec succès.", 'success')
|
||||
return HTTPFound(location=request.route_url('dossier_view', nodossier=nodossier) + '#tab_attaches')
|
||||
|
||||
if 'form.deleted' in request.params:
|
||||
delete_dem_note(request, nodossier, noligne)
|
||||
request.session.flash("'%s' a été supprimée avec succès." % note.libelle, 'success')
|
||||
return HTTPFound(location=request.route_url('dossier_view', nodossier=nodossier) + '#tab_attaches')
|
||||
|
||||
return {
|
||||
'page_title': "Devis en attente d'acceptation",
|
||||
'page_title': page_title,
|
||||
'url': url,
|
||||
'list_devis_en_att':list_devis_en_att,
|
||||
'message': message,
|
||||
'nodossier': nodossier,
|
||||
'note': note,
|
||||
}
|
||||
|
||||
@view_config(route_name='factures_en_att', renderer='../templates/dossier/factures_en_att.pt', permission='view')
|
||||
def dem_devis(request):
|
||||
@view_config(route_name='dessin_edit', renderer='../templates/dossier/dessin_edit.pt', permission='view')
|
||||
def dessin_edit(request):
|
||||
logged_in = request.authenticated_userid.upper()
|
||||
url = request.route_url('factures_en_att')
|
||||
nodossier = request.matchdict['nodossier']
|
||||
noligne = request.matchdict['noligne']
|
||||
url = request.route_url('dessin_edit', nodossier=nodossier, noligne=noligne)
|
||||
societe = nodossier[0:2]
|
||||
nochantier = nodossier[3:]
|
||||
|
||||
list_factures_en_att = get_factures_en_att(request)
|
||||
message = ""
|
||||
if noligne == '0':
|
||||
page_title = nodossier + ' : Nouveau dessin'
|
||||
else:
|
||||
page_title = nodossier + " : "
|
||||
|
||||
if 'form.submitted' in request.params:
|
||||
# get the base64-encoded-canvas image
|
||||
img_data = request.params["image"]
|
||||
img_data = img_data.replace('data:image/png;base64', '')
|
||||
img_data = img_data.replace(' ', '+')
|
||||
# convertir image de string en bytes
|
||||
img_bytes = img_data.encode('utf-8')
|
||||
decoded_image = base64.decodebytes(img_bytes)
|
||||
|
||||
# lire le nombre de dessins déjà créés
|
||||
nb_dessins = get_nb_dessins(request, nodossier)
|
||||
# fabriquer le nom du dessin
|
||||
filename = '%s-DD%s-%s' % (societe, nochantier, 'DESSIN No ' + str(nb_dessins + 1) + ".png")
|
||||
path = '%s/%s/%s' % (request.registry.settings['mondumas.devfac_dir'], societe, nochantier)
|
||||
# créer le répertoire du chantier
|
||||
os.makedirs(path, exist_ok=True)
|
||||
|
||||
filepath = os.path.join('%s/%s' % (path, filename))
|
||||
# ecrire l'image dans un fichier PNG
|
||||
f = open(filepath, "wb")
|
||||
f.write(decoded_image)
|
||||
f.close
|
||||
|
||||
filesize = round(os.path.getsize(filepath) / 1024)
|
||||
insert_dossier_attaches(request, '%s-%s' % (societe, nochantier), 0, 'FRN', filename, '%s Ko' % str(filesize), logged_in)
|
||||
|
||||
request.session.flash('%s est enregistré dans les DOC. TECHNIQUES.' % filename, 'success')
|
||||
return HTTPFound(location=request.route_url('dossier_view', nodossier=nodossier) + '#tab_attaches')
|
||||
|
||||
return {
|
||||
'page_title': page_title,
|
||||
'url': url,
|
||||
'message': message,
|
||||
'nodossier': nodossier,
|
||||
}
|
||||
|
||||
@view_config(route_name='dern_suivis', renderer='../templates/dossier/dern_suivis.pt', permission='view')
|
||||
def dern_suivis(request):
|
||||
# lire les derniers suivis créés par les attachés de clientèle
|
||||
items = get_derniers_suivis(request)
|
||||
|
||||
return {
|
||||
'page_title': "Factures en attente de réglement",
|
||||
'url': url,
|
||||
'list_factures_en_att':list_factures_en_att,
|
||||
'page_title': 'Derniers suivis créés',
|
||||
'items': items,
|
||||
}
|
||||
@@ -11,8 +11,6 @@ from pyramid.httpexceptions import (
|
||||
HTTPForbidden,
|
||||
)
|
||||
|
||||
from pyramid_mailer import get_mailer
|
||||
from pyramid_mailer.message import Message, Attachment
|
||||
from datetime import *
|
||||
from dateutil.relativedelta import *
|
||||
from docutils.core import publish_parts
|
||||
@@ -37,14 +35,7 @@ def parametres(request):
|
||||
'logged_in': logged_in,
|
||||
}
|
||||
|
||||
@view_config(route_name='infrastructure', renderer='../templates/parametres/infrastructure.pt', permission='manage')
|
||||
def infrastructure(request):
|
||||
logged_in = request.authenticated_userid.lower()
|
||||
|
||||
return {
|
||||
'page_title': "Infrastructure",
|
||||
'logged_in': logged_in,
|
||||
}
|
||||
|
||||
@view_config(route_name='users', renderer='../templates/parametres/users.pt', permission='manage')
|
||||
def users(request):
|
||||
@@ -58,17 +49,8 @@ def users(request):
|
||||
etat = 'Inactif'
|
||||
else:
|
||||
etat = ''
|
||||
|
||||
if item.access == 5:
|
||||
role = 'Gestion'
|
||||
elif item.access == 8:
|
||||
role = 'Compta'
|
||||
elif item.access == 9:
|
||||
role = 'Admin'
|
||||
else:
|
||||
role = 'Production'
|
||||
|
||||
d = (item.CD_UTI, item.NOM, item.email, item.agenda, role, item.societe, etat)
|
||||
|
||||
d = (item.CD_UTI, item.NOM, item.email, item.agenda, item.libelle, item.societe, etat)
|
||||
liste.append(d)
|
||||
|
||||
return {
|
||||
@@ -113,7 +95,8 @@ def user_edit(request):
|
||||
cd_uti = request.matchdict['cd_uti']
|
||||
url = request.route_url('user_edit', cd_uti=cd_uti)
|
||||
message = ''
|
||||
access = ["0 | Production", "5 | Gestion", "8 | Comptabilité", "9 | Administration"]
|
||||
|
||||
access = get_p_acces(request)
|
||||
societes = ['PE','ME','PL','PO','CD']
|
||||
|
||||
if cd_uti == '0':
|
||||
@@ -122,7 +105,7 @@ def user_edit(request):
|
||||
individu['CD_UTI'] = '0'
|
||||
individu['NOM'] = ''
|
||||
individu['email'] = ''
|
||||
individu['access'] = '0 | Production'
|
||||
individu['access'] = 0
|
||||
individu['actif'] = 1
|
||||
individu['agenda'] = ''
|
||||
individu['societe'] = ''
|
||||
@@ -174,10 +157,13 @@ def dashboard(request):
|
||||
|
||||
# lire le log de nuit
|
||||
log_nuit = get_log_nuit(request)
|
||||
# lire les dernières modifs dans tables
|
||||
last_modified = get_last_modified(request)
|
||||
|
||||
return {
|
||||
'page_title': "Tableau de bord",
|
||||
'log_nuit': log_nuit,
|
||||
'last_modified': last_modified,
|
||||
}
|
||||
|
||||
@view_config(route_name='rappels_rdv', renderer='../templates/parametres/rappels_rdv.pt', permission='manage')
|
||||
@@ -393,7 +379,11 @@ def articles(request):
|
||||
def article_edit(request):
|
||||
ref = request.matchdict['ref']
|
||||
url = request.route_url('article_edit', ref=ref)
|
||||
logged_in = request.authenticated_userid
|
||||
logged_in = request.authenticated_userid.upper()
|
||||
# modification d'article temporairement désactivé
|
||||
# if logged_in != 'CAO':
|
||||
# return HTTPFound(location=request.route_url('articles'))
|
||||
|
||||
message = ''
|
||||
|
||||
familles = ["Article", "Texte"]
|
||||
@@ -415,7 +405,7 @@ def article_edit(request):
|
||||
page_title= 'Nouvel article'
|
||||
else:
|
||||
# lire l'article
|
||||
item = get_article(request, 'REF', ref)
|
||||
item = get_article(request, ref)
|
||||
if not item:
|
||||
request.session.flash("article non trouvé : %s" % ref, 'warning')
|
||||
return HTTPFound(location=request.route_url('articles'))
|
||||
@@ -628,7 +618,7 @@ def societes(request):
|
||||
liste=[]
|
||||
for item in items:
|
||||
modif_le = item.modif_le.strftime('%d/%m/%Y')
|
||||
d = (item.societe, item.NOM, item.email_from, item.TEL, item.bic, item.iban, modif_le)
|
||||
d = (item.societe, item.NOM, item.email_from, item.TEL, item.email_cci, item.iban, modif_le)
|
||||
liste.append(d)
|
||||
|
||||
|
||||
@@ -687,7 +677,7 @@ def societe_edit(request):
|
||||
def tarifs(request):
|
||||
|
||||
groupe = request.matchdict['groupe']
|
||||
groupes = ["AXA", "MAIF", "TEXTE"]
|
||||
groupes = ["AXA", "MAIF", "IMH", "TEXTE"]
|
||||
|
||||
# si table a changé
|
||||
if 'groupe' in request.params:
|
||||
@@ -699,7 +689,7 @@ def tarifs(request):
|
||||
# construire la liste
|
||||
liste=[]
|
||||
for item in items:
|
||||
d = (item.ref, item.libelle, item.unite, to_euro(item.prixht), item.modif_le.strftime("%d-%m-%Y"))
|
||||
d = (item.ref, item.famille, item.libelle[:100], to_euro(item.prixht), item.unite, item.modif_le.strftime("%d-%m-%Y"))
|
||||
liste.append(d)
|
||||
|
||||
return {
|
||||
@@ -775,7 +765,7 @@ def tarifs_import(request):
|
||||
url = request.route_url("tarifs_import")
|
||||
message = ''
|
||||
|
||||
groupes = ['AXA','MAIF']
|
||||
groupes = ['AXA','MAIF', 'IMH']
|
||||
groupe = 'AXA'
|
||||
|
||||
|
||||
@@ -794,39 +784,134 @@ def tarifs_import(request):
|
||||
# readxl returns a pylightxl database that holds all worksheets and its data
|
||||
book = xlrd.open_workbook(temp_file)
|
||||
|
||||
# lire la 1ère feuille et contôler que c'est fichier AXA
|
||||
sh = book.sheet_by_index(0)
|
||||
ctl_cellA1 = sh.cell_value(rowx=0, colx=0)
|
||||
if ctl_cellA1 != 'PEN_2_MISEEN':
|
||||
request.session.flash(temp_file + " -> Ce fichier ne semble pas être un tarif AXA", 'danger')
|
||||
return HTTPFound(location=url)
|
||||
|
||||
# import pdb;pdb.set_trace()
|
||||
for nsheet in range(book.nsheets):
|
||||
# pour chaque feuille dans le tableau XLS
|
||||
sh = book.sheet_by_index(nsheet)
|
||||
for rx in range(sh.nrows):
|
||||
ref = sh.cell_value(rx, 0).strip()
|
||||
# ligne ayant un ref de tarifs ?
|
||||
if len(ref) >= 12 and len(ref) < 15 and ref[3]=='_':
|
||||
new_values = {}
|
||||
new_values['groupe'] = groupe
|
||||
new_values['ref'] = ref
|
||||
new_values['famille'] = ref[0:3]
|
||||
libelle = sh.cell_value(rx, 1)
|
||||
new_values['libelle'] = libelle.replace(' F+P M²', '').replace(' F+P U', '')
|
||||
new_values['prixht'] = sh.cell_value(rx, 5)
|
||||
# col Unité renseigné ?
|
||||
if len(sh.cell_value(rx, 3)) > 0:
|
||||
unite = sh.cell_value(rx, 3)
|
||||
else:
|
||||
if sh.cell_value(rx, 1).find(' M2'):
|
||||
unite = 'M²'
|
||||
if groupe == "AXA":
|
||||
# lire la 1ère feuille et contôler que c'est fichier AXA
|
||||
sh = book.sheet_by_index(0)
|
||||
ctl_cellA1 = sh.cell_value(rowx=0, colx=0)
|
||||
if ctl_cellA1 != 'PEN_2_MISEEN':
|
||||
request.session.flash(temp_file + " -> Ce fichier ne semble pas être un tarif AXA", 'danger')
|
||||
return HTTPFound(location=url)
|
||||
|
||||
# import pdb;pdb.set_trace()
|
||||
for nsheet in range(book.nsheets):
|
||||
# pour chaque feuille dans le tableau XLS
|
||||
sh = book.sheet_by_index(nsheet)
|
||||
for rx in range(sh.nrows):
|
||||
ref = sh.cell_value(rx, 0).strip()
|
||||
# ligne ayant un ref de tarifs ?
|
||||
if len(ref) >= 12 and len(ref) < 15 and ref[3]=='_':
|
||||
new_values = {}
|
||||
new_values['groupe'] = groupe
|
||||
new_values['ref'] = ref
|
||||
new_values['famille'] = ref[0:3]
|
||||
libelle = sh.cell_value(rx, 1)
|
||||
new_values['libelle'] = libelle.replace(' F+P M²', '').replace(' F+P U', '')
|
||||
new_values['prixht'] = sh.cell_value(rx, 5)
|
||||
# col Unité renseigné ?
|
||||
if len(sh.cell_value(rx, 3)) > 0:
|
||||
unite = sh.cell_value(rx, 3)
|
||||
else:
|
||||
unite = 'U'
|
||||
new_values['unite'] = unite
|
||||
|
||||
update_tarif(request, '0', new_values)
|
||||
if sh.cell_value(rx, 1).find(' M2'):
|
||||
unite = 'M²'
|
||||
else:
|
||||
unite = 'U'
|
||||
new_values['unite'] = unite
|
||||
update_tarif(request, '0', new_values)
|
||||
elif groupe == "MAIF":
|
||||
# lire la 1ère feuille et contôler que c'est fichier MAIF
|
||||
sh = book.sheet_by_index(0)
|
||||
ctl_cellA6 = sh.cell_value(rowx=5, colx=0)
|
||||
if ctl_cellA6 != 'PEN_01':
|
||||
request.session.flash(temp_file + " -> Ce fichier ne semble pas être un tarif MAIF", 'danger')
|
||||
return HTTPFound(location=url)
|
||||
|
||||
# import pdb;pdb.set_trace()
|
||||
for nsheet in range(book.nsheets):
|
||||
# pour chaque feuille dans le tableau XLS
|
||||
sh = book.sheet_by_index(nsheet)
|
||||
for rx in range(sh.nrows):
|
||||
ref = sh.cell_value(rx, 0).strip()
|
||||
# ligne ayant un ref de tarifs ?
|
||||
if len(ref) >= 6 and len(ref) < 15 and ref[3]=='_':
|
||||
new_values = {}
|
||||
new_values['groupe'] = groupe
|
||||
new_values['ref'] = ref
|
||||
new_values['famille'] = ref[0:3]
|
||||
libelle = sh.cell_value(rx, 1)
|
||||
new_values['libelle'] = libelle.replace(' F+P M²', '').replace(' F+P U', '')
|
||||
if sh.cell_type(rx, 5) == 2:
|
||||
new_values['prixht'] = sh.cell_value(rx, 5)
|
||||
# col Unité renseigné ?
|
||||
if sh.cell_type(rx, 3) != 0:
|
||||
unite = sh.cell_value(rx, 3)
|
||||
else:
|
||||
if sh.cell_value(rx, 1).find(' m2'):
|
||||
unite = 'M²'
|
||||
else:
|
||||
unite = 'U'
|
||||
new_values['unite'] = unite
|
||||
update_tarif(request, '0', new_values)
|
||||
elif groupe == "IMH":
|
||||
# lire la 1ère feuille et contôler que c'est fichier IMH
|
||||
sh = book.sheet_by_index(0)
|
||||
ctl_cellA1 = sh.cell_value(rowx=0, colx=0)
|
||||
if ctl_cellA1 != 'CO-FORI0':
|
||||
request.session.flash(temp_file + " -> Ce fichier ne semble pas être un tarif IMH", 'danger')
|
||||
return HTTPFound(location=url)
|
||||
|
||||
# import pdb;pdb.set_trace()
|
||||
for nsheet in range(book.nsheets):
|
||||
# pour chaque feuille dans le tableau XLS
|
||||
sh = book.sheet_by_index(nsheet)
|
||||
libCol, prixCol, unitCol = 1, 5, 2
|
||||
|
||||
if nsheet > 73:
|
||||
libCol, prixCol, unitCol = 2, 4, 3
|
||||
|
||||
for rx in range(sh.nrows):
|
||||
ref = ""
|
||||
if sh.cell_type(rx, 0) != 2:
|
||||
ref = sh.cell_value(rx, 0).strip()
|
||||
# ligne ayant un ref de tarifs ?
|
||||
if len(ref) >= 8 and len(ref) < 15 and (ref[2] == '-' or ref[3] == '-'):
|
||||
new_values = {}
|
||||
new_values['groupe'] = groupe
|
||||
new_values['ref'] = ref
|
||||
|
||||
if ref[2] == '-':
|
||||
new_values['famille'] = ref[0:2]
|
||||
else:
|
||||
new_values['famille'] = ref[0:3]
|
||||
|
||||
if '\n' in sh.cell_value(rx, libCol) and nsheet < 73:
|
||||
libelleSP = sh.cell_value(rx, libCol).split('\n') # split string with \n as separator if it exists
|
||||
|
||||
if libelleSP[1][0] == '(':
|
||||
libelle = ' '.join(libelleSP[2:]) # if the first character of the second elt is '(' then we remove it
|
||||
else:
|
||||
libelle = ' '.join(libelleSP[1:])
|
||||
else:
|
||||
libelle = sh.cell_value(rx, libCol)
|
||||
|
||||
if len(libelle) > 400:
|
||||
new_values['libelle'] = 'DATA TOO LONG ! SEE THE ADMIN FOR FURTHER INFOS'
|
||||
else:
|
||||
new_values['libelle'] = libelle
|
||||
|
||||
unit = sh.cell_value(rx, unitCol)
|
||||
if unit == 'DEVIS' or 'FORFAIT URGENCE' in unit:
|
||||
new_values['prixht'] = 0
|
||||
else:
|
||||
if sh.cell_type(rx, prixCol) != 2:
|
||||
new_values['prixht'] = 0
|
||||
else:
|
||||
new_values['prixht'] = sh.cell_value(rx, prixCol)
|
||||
# col Unité renseigné ?
|
||||
if 'FORFAIT URGENCE' in unit:
|
||||
unit = 'F. URG.'
|
||||
new_values['unite'] = unit
|
||||
update_tarif(request, '0', new_values)
|
||||
|
||||
|
||||
request.session.flash("Le fichier PDF a été importé avec succès.", 'success')
|
||||
return HTTPFound(location=url)
|
||||
|
||||
@@ -176,8 +176,7 @@ def ca_groupes(request):
|
||||
print(datedeb)
|
||||
chart_ca_12m = []
|
||||
# titre des colonnes
|
||||
chart_ca_12m.append(('Mois', 'AXA', { 'type':'string','role': 'tooltip'},'DOMUS', { 'type':'string','role': 'tooltip'},
|
||||
'GMF', { 'type':'string','role': 'tooltip'},'MACIF', { 'type':'string','role': 'tooltip'}, 'MAIF', { 'type':'string','role': 'tooltip'}))
|
||||
chart_ca_12m.append(('Mois', 'MAIF', { 'type':'string','role': 'tooltip'}, 'MACIF', { 'type':'string','role': 'tooltip'}, 'GMF', { 'type':'string','role': 'tooltip'},'DOMUS', { 'type':'string','role': 'tooltip'}, 'AXA', { 'type':'string','role': 'tooltip'}))
|
||||
title = 'CA sur 12 mois'
|
||||
for item in items:
|
||||
date_aff = item.date[:3] + ' ' + item.date[-4:]
|
||||
@@ -188,7 +187,7 @@ def ca_groupes(request):
|
||||
tooltipGMF = "GMF - " + date_aff + ' \nCA: '+str(item.GMF_ca) + ' €\nDossiers: '+str(round(item.GMF_nb))
|
||||
tooltipMACIF = "MACIF - " + date_aff + ' \nCA: '+str(item.MACIF_ca) + ' €\nDossiers: '+str(round(item.MACIF_nb))
|
||||
# ('+str(item.population)+')/n'
|
||||
d = (item.mois[:3], float(item.AXA_ca), tooltipAXA, float(item.DOMUS_ca), tooltipDOMUS, float(item.GMF_ca), tooltipGMF, float(item.MACIF_ca), tooltipMACIF, float(item.MAIF_ca), tooltipMAIF)
|
||||
d = (item.mois[:3], float(item.MAIF_ca), tooltipMAIF, float(item.MACIF_ca), tooltipMACIF, float(item.GMF_ca), tooltipGMF, float(item.DOMUS_ca), tooltipDOMUS, float(item.AXA_ca), tooltipAXA)
|
||||
chart_ca_12m.append(d)
|
||||
|
||||
# debut = aujourd'hui - 11 mois
|
||||
@@ -203,9 +202,6 @@ def ca_groupes(request):
|
||||
chart_ca_3y_1.append(('Groupe', 'CA', { 'type':'string','role': 'tooltip'}))
|
||||
chart_ca_3y_2.append(('Groupe', 'CA', { 'type':'string','role': 'tooltip'}))
|
||||
chart_ca_3y_3.append(('Groupe', 'CA', { 'type':'string','role': 'tooltip'}))
|
||||
title1 = 'CA ' + str(thisyear - 2)
|
||||
title2 = 'CA ' + str(thisyear - 1)
|
||||
title3 = 'CA ' + str(thisyear)
|
||||
for item in items:
|
||||
# construire la liste pour donut cible
|
||||
tooltip_y1 = item.groupe + ' \nCA: '+str(item.Annee1) + ' €\nDossiers: '+str(round(item.Count1))
|
||||
@@ -219,6 +215,32 @@ def ca_groupes(request):
|
||||
d3 = (item.groupe, float(item.Annee3), tooltip_y3)
|
||||
chart_ca_3y_3.append(d3)
|
||||
|
||||
# lire les CA par mois
|
||||
items = get_ca_groupe_3y_with_others(request, societe, thisyear)
|
||||
chart_ca_3y_1_x = []
|
||||
chart_ca_3y_2_x = []
|
||||
chart_ca_3y_3_x = []
|
||||
# titre des colonnes
|
||||
chart_ca_3y_1_x.append(('Groupe', 'CA', { 'type':'string','role': 'tooltip'}))
|
||||
chart_ca_3y_2_x.append(('Groupe', 'CA', { 'type':'string','role': 'tooltip'}))
|
||||
chart_ca_3y_3_x.append(('Groupe', 'CA', { 'type':'string','role': 'tooltip'}))
|
||||
annee1 = str(thisyear - 2)
|
||||
annee2 = str(thisyear - 1)
|
||||
annee3 = str(thisyear)
|
||||
for item in items:
|
||||
# construire la liste pour donut cible
|
||||
tooltip_y1 = item.groupe + ' \nCA: '+str(item.Annee1) + ' €\nDossiers: '+str(round(item.Count1))
|
||||
tooltip_y2 = item.groupe + ' \nCA: '+str(item.Annee2) + ' €\nDossiers: '+str(round(item.Count1))
|
||||
tooltip_y3 = item.groupe + ' \nCA: '+str(item.Annee3) + ' €\nDossiers: '+str(round(item.Count1))
|
||||
# ('+str(item.population)+')/n'
|
||||
d1_x = (item.groupe, float(item.Annee1), tooltip_y1)
|
||||
chart_ca_3y_1_x.append(d1_x)
|
||||
d2_x = (item.groupe, float(item.Annee2), tooltip_y2)
|
||||
chart_ca_3y_2_x.append(d2_x)
|
||||
d3_x = (item.groupe, float(item.Annee3), tooltip_y3)
|
||||
chart_ca_3y_3_x.append(d3_x)
|
||||
|
||||
|
||||
return {
|
||||
'page_title': "CA par groupe",
|
||||
'url': url,
|
||||
@@ -226,10 +248,13 @@ def ca_groupes(request):
|
||||
'chart_ca_3y_1': json.dumps(chart_ca_3y_1),
|
||||
'chart_ca_3y_2': json.dumps(chart_ca_3y_2),
|
||||
'chart_ca_3y_3': json.dumps(chart_ca_3y_3),
|
||||
'chart_ca_3y_1_x': json.dumps(chart_ca_3y_1_x),
|
||||
'chart_ca_3y_2_x': json.dumps(chart_ca_3y_2_x),
|
||||
'chart_ca_3y_3_x': json.dumps(chart_ca_3y_3_x),
|
||||
'title': title,
|
||||
'title1': title1,
|
||||
'title2': title2,
|
||||
'title3': title3,
|
||||
'annee1': annee1,
|
||||
'annee2': annee2,
|
||||
'annee3': annee3,
|
||||
'societes': societes,
|
||||
'societe': societe,
|
||||
}
|
||||
@@ -254,9 +279,9 @@ def ca_clients(request):
|
||||
print(datedeb)
|
||||
chart_ca_12m = []
|
||||
# titre des colonnes
|
||||
chart_ca_12m.append(('Mois', 'ASSURANCES', { 'type':'string','role': 'tooltip'}, 'EXPERTS', { 'type':'string','role': 'tooltip'},
|
||||
'GROUPEMENT', { 'type':'string','role': 'tooltip'}, 'PARTICULIER', { 'type':'string','role': 'tooltip'},
|
||||
'REGIES', { 'type':'string','role': 'tooltip'}, 'SOCIETE', { 'type':'string','role': 'tooltip'}))
|
||||
chart_ca_12m.append(('Mois', 'SOCIETE', { 'type':'string','role': 'tooltip'}, 'REGIES', { 'type':'string','role': 'tooltip'},
|
||||
'PARTICULIER', { 'type':'string','role': 'tooltip'}, 'GROUPEMENT', { 'type':'string','role': 'tooltip'}, 'EXPERTS',
|
||||
{ 'type':'string','role': 'tooltip'}, 'ASSURANCES', { 'type':'string','role': 'tooltip'}))
|
||||
title = 'CA sur 12 mois'
|
||||
for item in items:
|
||||
date_aff = item.date[:3] + ' ' + item.date[-4:]
|
||||
@@ -268,8 +293,8 @@ def ca_clients(request):
|
||||
tooltipR = date_aff + ' \nCA: '+str(item.R_ca) + ' €\nDossiers: '+str(round(item.R_nb))
|
||||
tooltipS = date_aff + ' \nCA: '+str(item.S_ca) + ' €\nDossiers: '+str(round(item.S_nb))
|
||||
# ('+str(item.population)+')/n'
|
||||
d = (item.mois[:3], float(item.A_ca), tooltipA, float(item.E_ca), tooltipE, float(item.G_ca), tooltipG,
|
||||
float(item.P_ca), tooltipP, float(item.R_ca), tooltipR, float(item.S_ca), tooltipS)
|
||||
d = (item.mois[:3], float(item.S_ca), tooltipS, float(item.R_ca), tooltipR, float(item.P_ca), tooltipP,
|
||||
float(item.G_ca), tooltipG, float(item.E_ca), tooltipE, float(item.A_ca), tooltipA)
|
||||
chart_ca_12m.append(d)
|
||||
|
||||
# debut = aujourd'hui - 11 mois
|
||||
@@ -379,3 +404,92 @@ def delais_pourcentage(request):
|
||||
'groupe': groupe,
|
||||
}
|
||||
|
||||
@view_config(route_name='pourcentage_devis', renderer='../templates/stats/pourcentage_devis.pt', permission='view')
|
||||
def pourcentage_devis(request):
|
||||
|
||||
societe = request.matchdict['societe']
|
||||
url = request.route_url('delais_pourcentage', societe = societe)
|
||||
|
||||
# année aujourd'hui
|
||||
thisyear = date.today().year
|
||||
|
||||
societes = ['PE','ME','PL']
|
||||
# si societe a été changé par le user
|
||||
if 'societe' in request.params:
|
||||
societe = request.params["societe"]
|
||||
|
||||
# lire le nb de devis facturés
|
||||
items = get_nb_devis_fact(request, societe, thisyear)
|
||||
chart_devis_y1 = []
|
||||
chart_devis_y2 = []
|
||||
chart_devis_y3 = []
|
||||
# titre des colonnes
|
||||
chart_devis_y1.append(('Catégorie', 'Nb Devis facturés'))
|
||||
chart_devis_y2.append(('Catégorie', 'Nb Devis facturés'))
|
||||
chart_devis_y3.append(('Catégorie', 'Nb Devis facturés'))
|
||||
title_devis1 = 'NB DEVIS ' + str(thisyear - 2)
|
||||
title_devis2 = 'NB DEVIS ' + str(thisyear - 1)
|
||||
title_devis3 = 'NB DEVIS ' + str(thisyear)
|
||||
for item in items:
|
||||
d1 = ('Nb de devis facturés', float(item.devis_fact_y1))
|
||||
chart_devis_y1.append(d1)
|
||||
d1 = ('Nb de devis non facturés', float(item.devis_non_fact_y1))
|
||||
chart_devis_y1.append(d1)
|
||||
|
||||
d2 = ('Nb de devis facturés', float(item.devis_fact_y2))
|
||||
chart_devis_y2.append(d2)
|
||||
d2 = ('Nb de devis non facturés', float(item.devis_non_fact_y2))
|
||||
chart_devis_y2.append(d2)
|
||||
|
||||
d3 = ('Nb de devis facturés', float(item.devis_fact_y3))
|
||||
chart_devis_y3.append(d3)
|
||||
d3 = ('Nb de devis non facturés', float(item.devis_non_fact_y3))
|
||||
chart_devis_y3.append(d3)
|
||||
|
||||
# lire le nb de devis facturés
|
||||
items = get_nb_fact_with_devis(request, societe, thisyear)
|
||||
chart_fact_y1 = []
|
||||
chart_fact_y2 = []
|
||||
chart_fact_y3 = []
|
||||
# titre des colonnes
|
||||
chart_fact_y1.append(('Catégorie', 'Nb Factures avec devis'))
|
||||
chart_fact_y2.append(('Catégorie', 'Nb Factures avec devis'))
|
||||
chart_fact_y3.append(('Catégorie', 'Nb Factures avec devis'))
|
||||
title_fact1 = 'NB FACTURES ' + str(thisyear - 2)
|
||||
title_fact2 = 'NB FACTURES ' + str(thisyear - 1)
|
||||
title_fact3 = 'NB FACTURES ' + str(thisyear)
|
||||
for item in items:
|
||||
d1 = ("Nb de factures avec devis", float(item.fact_w_devis_y1))
|
||||
chart_fact_y1.append(d1)
|
||||
d1 = ("Nb de factures sans devis", float(item.fact_wo_devis_y1))
|
||||
chart_fact_y1.append(d1)
|
||||
|
||||
d2 = ("Nb de factures avec devis", float(item.fact_w_devis_y2))
|
||||
chart_fact_y2.append(d2)
|
||||
d2 = ("Nb de factures sans devis", float(item.fact_wo_devis_y2))
|
||||
chart_fact_y2.append(d2)
|
||||
|
||||
d3 = ("Nb de factures avec devis", float(item.fact_w_devis_y3))
|
||||
chart_fact_y3.append(d3)
|
||||
d3 = ("Nb de factures sans devis", float(item.fact_wo_devis_y3))
|
||||
chart_fact_y3.append(d3)
|
||||
|
||||
return {
|
||||
'page_title': "Nombre de devis facturés et de factures avec devis",
|
||||
'url': url,
|
||||
'chart_devis_y1': json.dumps(chart_devis_y1),
|
||||
'chart_devis_y2': json.dumps(chart_devis_y2),
|
||||
'chart_devis_y3': json.dumps(chart_devis_y3),
|
||||
'chart_fact_y1': json.dumps(chart_fact_y1),
|
||||
'chart_fact_y2': json.dumps(chart_fact_y2),
|
||||
'chart_fact_y3': json.dumps(chart_fact_y3),
|
||||
'title_devis1': title_devis1,
|
||||
'title_devis2': title_devis2,
|
||||
'title_devis3': title_devis3,
|
||||
'title_fact1': title_fact1,
|
||||
'title_fact2': title_fact2,
|
||||
'title_fact3': title_fact3,
|
||||
'societes': societes,
|
||||
'societe': societe,
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,10 @@ from pyramid.view import (
|
||||
from pyramid_mailer import get_mailer
|
||||
from pyramid_mailer.message import Message, Attachment
|
||||
|
||||
import smtplib
|
||||
import ssl
|
||||
from email.message import EmailMessage
|
||||
|
||||
from time import sleep
|
||||
from datetime import *
|
||||
from dateutil.relativedelta import *
|
||||
@@ -31,6 +35,7 @@ def batch_nuit(request):
|
||||
|
||||
# ----- effacer le log
|
||||
truncate_log(request)
|
||||
|
||||
|
||||
# ----- PURGE des données obsolètes
|
||||
insert_log(request, 'PURGE','- Début PURGE DES DONNEES OBSOLETES')
|
||||
@@ -39,15 +44,17 @@ def batch_nuit(request):
|
||||
if TODAY.day == 1 :
|
||||
# données de + 10 ans
|
||||
until_date = date(TODAY.year - 10, TODAY.month, 1)
|
||||
# purge_mensuelle(request, until_date)
|
||||
# purge_clients(request)
|
||||
# delete_orphan_attached_files(request)
|
||||
purge_mensuelle(request, until_date)
|
||||
purge_clients(request)
|
||||
delete_orphan_attached_files(request)
|
||||
|
||||
"""
|
||||
# ----- MAJ STATS DELAIS de traitements des dossiers sur 1 an
|
||||
insert_log(request, 'STATS', '- Début MAJ STATS DES PERFORMANCES')
|
||||
update_chantiers_delais(request, date(TODAY.year, TODAY.month, 1))
|
||||
update_chantiers_delais(request, date(TODAY.year, TODAY.month-1, 1))
|
||||
update_chantiers_delais(request, date(TODAY.year, TODAY.month-2, 1))
|
||||
datejour = TODAY
|
||||
for i in range(12):
|
||||
update_chantiers_delais(request, datejour)
|
||||
datejour = datejour + relativedelta(months=-1)
|
||||
|
||||
societes = ['PL', 'ME', 'PE']
|
||||
datejour = TODAY
|
||||
@@ -58,12 +65,14 @@ def batch_nuit(request):
|
||||
update_stats_delais(request, societe, datejour.strftime('%Y-%m-%d'), groupe)
|
||||
# recule d'un mois
|
||||
datejour = datejour + relativedelta(months=-1)
|
||||
|
||||
"""
|
||||
# ----- RAPPELS DES RENDEZ-VOUS
|
||||
update_rappels(request)
|
||||
# attendre 3 secondes
|
||||
sleep(3)
|
||||
|
||||
|
||||
|
||||
# ----- envoyer les rappels
|
||||
notifier_rappels(request)
|
||||
|
||||
@@ -92,6 +101,7 @@ def notifier_rappels(request):
|
||||
# RDV ayant une heure
|
||||
date_heure = item.rdv_date.strftime('%d/%m/%Y - %H:%M')
|
||||
szBody = """
|
||||
<!DOCTYPE html>
|
||||
<html><body>
|
||||
<p>Bonjour %s %s,</p>
|
||||
<p>L’entreprise %s vous rappelle votre prochain rendez-vous, pris d'un commun accord</p>
|
||||
@@ -122,20 +132,51 @@ def notifier_rappels(request):
|
||||
return
|
||||
|
||||
def email_rappels(request, objet, corps, destinataire, societe):
|
||||
# import pdb;pdb.set_trace()
|
||||
# Create a secure SSL context
|
||||
context = ssl.create_default_context()
|
||||
smtp_server = "smtp.office365.com"
|
||||
smtp_port = 587 # For TLS
|
||||
# lire la societe
|
||||
soc = get_societes(request, societe)
|
||||
if soc:
|
||||
expediteur = soc.email_from
|
||||
smtp_user = soc.email_from
|
||||
smtp_pass = soc.email_cci
|
||||
else:
|
||||
expediteur = "peinture@entreprise-dumas.com"
|
||||
# envoyer le rappel
|
||||
error = send_mail(request, expediteur, destinataire, "[Ent. Dumas] " + objet, corps)
|
||||
smtp_user = request.registry.settings['mail.username']
|
||||
smtp_pass = request.registry.settings['mail.password']
|
||||
|
||||
# construire le message
|
||||
msg = EmailMessage()
|
||||
msg['Subject'] = "[Ent. Dumas] " + objet
|
||||
msg['From'] = smtp_user
|
||||
msg['To'] = destinataire
|
||||
# msg['Bcc'] = "phuoc@caotek.fr"
|
||||
msg.set_content(corps, subtype='html')
|
||||
|
||||
# Try to log in to server and send email
|
||||
err = ''
|
||||
try:
|
||||
server = smtplib.SMTP(smtp_server,smtp_port)
|
||||
server.starttls(context=context) # Secure the connection
|
||||
server.login(smtp_user, smtp_pass)
|
||||
# envoyer l'email
|
||||
server.send_message(msg)
|
||||
print("sendmail -> OK")
|
||||
|
||||
except Exception as e:
|
||||
# Just print(e) is cleaner and more likely what you want,
|
||||
# but if you insist on printing message specifically whenever possible...
|
||||
err = repr(e)[0:400]
|
||||
insert_log(request, 'MAILER', "- ERROR : %s, TO : %s" % (err, destinataire))
|
||||
finally:
|
||||
server.quit()
|
||||
|
||||
return error
|
||||
return len(err)
|
||||
|
||||
def email_rapport(request):
|
||||
NOW = datetime.now()
|
||||
corps = "<html><body><p>=============================================</p>"
|
||||
corps = "<!DOCTYPE html><html><body><p>=============================================</p>"
|
||||
corps += "<p>Rapport du traitement de nuit du " + NOW.strftime('%d/%m/%Y - %H:%M') + "</p>"
|
||||
corps += "<p>=============================================</p><p></p><p>"
|
||||
|
||||
@@ -147,24 +188,20 @@ def email_rapport(request):
|
||||
corps += "</p><p></p><p>=============================================</p><p></p>"
|
||||
|
||||
expediteur = request.registry.settings['mondumas.admin_email']
|
||||
destinataire = ["phuoc@caotek.fr","peinture@entreprise-dumas.com"]
|
||||
destinataire = ["phuoc@caotek.fr","peinture-dumas@entreprise-dumas.com"]
|
||||
send_mail(request, expediteur, destinataire, "[DEV_NUIT] Rapport des traitements de nuit", corps)
|
||||
|
||||
return
|
||||
|
||||
|
||||
def send_mail(request, expediteur, destinataires, objet, corps):
|
||||
body = """
|
||||
|
||||
%s
|
||||
""" % (corps)
|
||||
body_html = Attachment(data=corps, transfer_encoding="base64", disposition='inline')
|
||||
|
||||
message = Message(subject=objet,
|
||||
sender=expediteur,
|
||||
recipients=destinataires,
|
||||
html=body)
|
||||
html=body_html)
|
||||
mailer = get_mailer(request)
|
||||
# import pdb;pdb.set_trace()
|
||||
msg = ''
|
||||
try:
|
||||
mailer.send_immediately(message)
|
||||
@@ -172,14 +209,10 @@ def send_mail(request, expediteur, destinataires, objet, corps):
|
||||
except Exception as e:
|
||||
# Just print(e) is cleaner and more likely what you want,
|
||||
# but if you insist on printing message specifically whenever possible...
|
||||
if hasattr(e, 'message'):
|
||||
msg = e.message
|
||||
else:
|
||||
msg = e
|
||||
# logguer l'erreur
|
||||
msg = repr(e)[0:400]
|
||||
insert_log(request, 'MAILER', "- ERROR : %s, TO : %s" % (msg, destinataires))
|
||||
|
||||
return len(msg)
|
||||
return str(msg)
|
||||
|
||||
@view_config(route_name='batch_test')
|
||||
def batch_test(request):
|
||||
@@ -194,15 +227,25 @@ def batch_test(request):
|
||||
if par != 'Sansa5tark':
|
||||
return Response('Erreur : paramètre incorrect')
|
||||
|
||||
# à revoir
|
||||
update_chantiers_status(request)
|
||||
|
||||
|
||||
TODAY = date.today()
|
||||
until_date = date(TODAY.year - 10, TODAY.month, 1)
|
||||
# purge_mensuelle(request, until_date)
|
||||
# purge_clients(request)
|
||||
|
||||
# ------ UPDATE statut DEVIS COMMANDE
|
||||
# update_devis_statut_4(request)
|
||||
|
||||
# delete_orphan_attached_files(request)
|
||||
"""
|
||||
# ----- MAJ STATS DELAIS de traitements des dossiers sur 1 an
|
||||
datejour = TODAY
|
||||
for i in xrange(12):
|
||||
update_chantiers_delais(request, datejour)
|
||||
datejour = datejour + relativedelta(months=-1)
|
||||
|
||||
"""
|
||||
# données de + 10 ans
|
||||
until_date = date(TODAY.year - 10, TODAY.month, 1)
|
||||
purge_mensuelle(request, until_date)
|
||||
purge_clients(request)
|
||||
delete_orphan_attached_files(request)
|
||||
|
||||
# update_chantiers_delais(request, date(TODAY.year - 1, TODAY.month, 1))
|
||||
insert_log(request, 'TEST','- Fin -')
|
||||
@@ -239,42 +282,7 @@ def delete_orphan_attached_files(request):
|
||||
|
||||
if nbLus > 0:
|
||||
insert_log(request, 'DELETE', '%s : %d Répertoires lues, %d supprimées' % (ste, nbLus, nbSupp))
|
||||
|
||||
|
||||
def update_chantiers_status(request):
|
||||
"""
|
||||
Ce traitement parcourt tous les chantiers de chacune des 5 sociétés
|
||||
pour mettre à jour son STATUS selon l'avancement du chantier.
|
||||
"""
|
||||
|
||||
societes = ['PE','ME','PL','PO','CD']
|
||||
|
||||
for ste in societes:
|
||||
# lire tous les chantiers
|
||||
chantiers = get_all_chantiers(request, ste)
|
||||
for item in chantiers:
|
||||
status = item.STATUS
|
||||
# si le chantier est annulé, ne rien faire
|
||||
if status <= 10:
|
||||
# lire la dernière facture du chantier
|
||||
facture = get_last_facture(request, ste, item.NO_ID)
|
||||
if facture :
|
||||
# remonte le status de la facture ('','Régl part.', 'Réglée')
|
||||
status = facture.STATUS
|
||||
else:
|
||||
# lire le dernier devis du chantier ('','Commandé, 'Facturé')
|
||||
devis = get_last_devis(request, ste, item.NO_ID)
|
||||
if devis:
|
||||
# remonte le status de la facture
|
||||
status = devis.STATUS
|
||||
else:
|
||||
if item.DATEVI:
|
||||
status = 2
|
||||
|
||||
# maj le status du chantier
|
||||
if status != item.STATUS:
|
||||
update_chantier_status(request, ste, item.NO_ID, status)
|
||||
|
||||
|
||||
def update_chantiers_delais(request, date):
|
||||
"""
|
||||
Ce traitement calcul les delais :
|
||||
@@ -335,29 +343,30 @@ def update_chantiers_delais(request, date):
|
||||
update_chantier_delais(request, societe, item.NO_ID, delai_contact, delai_rdv, delai_devis, delai_facture)
|
||||
|
||||
def purge_clients(request):
|
||||
|
||||
# lire tous les clients
|
||||
clients = get_all_clients(request)
|
||||
for client in clients:
|
||||
dern_operation = None
|
||||
# lire la facture la + récente
|
||||
facture = get_last_facture_client(request, client.societe, client.CD_CLI)
|
||||
if facture:
|
||||
dern_operation = facture.DATE
|
||||
# lire le chantier le + récent
|
||||
chantier = get_last_chantier_client(request, client.societe, client.CD_CLI)
|
||||
if chantier:
|
||||
dern_operation = chantier.DATE
|
||||
else:
|
||||
# lire le réglemnet le + récent
|
||||
payment = get_last_payment_client(request, client.societe, client.CD_CLI)
|
||||
if payment:
|
||||
dern_operation = payment.DATE
|
||||
# lire le devis le + récent
|
||||
devis = get_last_devis_client(request, client.societe, client.CD_CLI)
|
||||
if devis:
|
||||
dern_operation = devis.DATE
|
||||
else:
|
||||
# lire le devis le + récent
|
||||
devis = get_last_devis_client(request, client.societe, client.CD_CLI)
|
||||
if devis:
|
||||
dern_operation = devis.DATE
|
||||
# lire le proforma le + récent
|
||||
proforma = get_last_proforma_client(request, client.societe, client.CD_CLI)
|
||||
if proforma:
|
||||
dern_operation = proforma.DATE
|
||||
else:
|
||||
# lire le chantier le + récent
|
||||
chantier = get_last_chantier_client(request, client.societe, client.CD_CLI)
|
||||
if chantier:
|
||||
dern_operation = chantier.DATE
|
||||
# lire la facture la + récente
|
||||
facture = get_last_facture_client(request, client.societe, client.CD_CLI)
|
||||
if facture:
|
||||
dern_operation = facture.DATE
|
||||
|
||||
# mémoriser dernière opération
|
||||
update_client_dern_operation(request, client.societe, client.CD_CLI, dern_operation)
|
||||
|
||||
@@ -20,21 +20,23 @@ pyramid.includes =
|
||||
sqlalchemy.url = mysql://phuoc:phuoc!@192.168.0.31/bddevfac?charset=utf8
|
||||
sqlalchemy.pool_recycle = 3600
|
||||
|
||||
mondumas.admin_email = sasdumas@entreprise-dumas.com
|
||||
mondumas.admin_email = peinture-dumas@entreprise-dumas.com
|
||||
mondumas.devfac_url = mondumas:static/DEVFAC/DOCS_ATTACHES/
|
||||
mondumas.devfac_dir = /DEVFAC14/DOCS_ATTACHES
|
||||
|
||||
# Mailer configuration
|
||||
mail.host = mail1.sfrbusinessteam.fr
|
||||
mail.port = 25
|
||||
mail.ssl = False
|
||||
# SMTP alinto quota limit 30 emails
|
||||
# mail.host = v5.alinto.net
|
||||
mail.host = smtp.office365.com
|
||||
mail.port = 587
|
||||
mail.tls = True
|
||||
mail.username = peinture-dumas@entreprise-dumas.com
|
||||
mail.password = Nar50611
|
||||
|
||||
# SMTP RELAY alinto
|
||||
# mail.host = gatewayxl.alinto.net
|
||||
# mail.port = 465
|
||||
# mail.ssl = True
|
||||
# ATTENTION : mail.username est utilisé dans rdf_view
|
||||
mail.username = peinture-dumas@entreprise-dumas.com
|
||||
mail.password = sasdumas
|
||||
# mail.username = smtpsasdumas@entreprise-dumas.com
|
||||
|
||||
|
||||
[server:main]
|
||||
use = egg:waitress#main
|
||||
|
||||
6
setup.py
6
setup.py
@@ -9,17 +9,17 @@ with open(os.path.join(here, 'CHANGES.txt')) as f:
|
||||
CHANGES = f.read()
|
||||
|
||||
requires = [
|
||||
'pyramid',
|
||||
'pyramid == 1.10',
|
||||
'pyramid_chameleon',
|
||||
'pyramid_debugtoolbar',
|
||||
'pyramid_layout',
|
||||
'pyramid_mailer',
|
||||
'pyramid_tm',
|
||||
'SQLAlchemy',
|
||||
'SQLAlchemy == 1.2.19',
|
||||
'transaction',
|
||||
'zope.sqlalchemy == 1.1',
|
||||
'waitress',
|
||||
'mysqlclient',
|
||||
'mysqlclient == 1.4',
|
||||
'docutils',
|
||||
'pdfkit',
|
||||
'python-dateutil',
|
||||
|
||||
35
smtp-TLS-office365-dumas.py
Normal file
35
smtp-TLS-office365-dumas.py
Normal file
@@ -0,0 +1,35 @@
|
||||
#!/usr/bin/env python3
|
||||
import smtplib
|
||||
import ssl
|
||||
|
||||
# Fabrication du corps du email_passwordMessage
|
||||
sender = "peinture@entreprise-dumas.com"
|
||||
receiver = "phuoc@caotek.fr"
|
||||
message = """\
|
||||
Subject: Test d'envoi d'un message en TLS
|
||||
|
||||
Voici le contenu du message. Phuoc Cao"""
|
||||
|
||||
# Create a secure SSL context
|
||||
context = ssl.create_default_context()
|
||||
smtp_server = "smtp.office365.com"
|
||||
smtp_port = 587 # For TLS
|
||||
smtp_user = "peinture-dumas@entreprise-dumas.com"
|
||||
smtp_pass = "Nar50611"
|
||||
|
||||
# Try to log in to server and send email
|
||||
try:
|
||||
server = smtplib.SMTP(smtp_server,smtp_port)
|
||||
# server.ehlo() # Can be omitted
|
||||
server.starttls(context=context) # Secure the connection
|
||||
# server.ehlo() # Can be omitted
|
||||
server.login(smtp_user, smtp_pass)
|
||||
# envoyer l'email
|
||||
server.sendmail(sender, receiver, message)
|
||||
print("sendmail -> OK")
|
||||
|
||||
except Exception as e:
|
||||
# Print any error messages to stdout
|
||||
print(e)
|
||||
finally:
|
||||
server.quit()
|
||||
Reference in New Issue
Block a user