1
|
# -*- coding: utf-8 -*-
|
2
|
#!/usr/bin/env python
|
3
|
|
4
|
"""
|
5
|
Script de contrôle et de reporting
|
6
|
"""
|
7
|
|
8
|
import os
|
9
|
import tempfile
|
10
|
|
11
|
from datetime import datetime, timedelta
|
12
|
from datetime import date as date_setter
|
13
|
|
14
|
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "calebasse.settings")
|
15
|
|
16
|
from calebasse.facturation.models import Invoice
|
17
|
from calebasse.actes.models import Act, ValidationMessage
|
18
|
from calebasse.dossiers.models import PatientRecord
|
19
|
from calebasse.agenda.models import EventWithAct
|
20
|
|
21
|
SEND_MAIL = "mates@entrouvert.com"
|
22
|
OUTPUT_DIR = "./"
|
23
|
PREFIX = "analyse"
|
24
|
|
25
|
ANALYSE_SDT = datetime(2013,1,1)
|
26
|
|
27
|
ALWAYS_SEND_MAIL = True
|
28
|
|
29
|
LOOKUP_NEW = True
|
30
|
END_LOOKUP = datetime.today()
|
31
|
|
32
|
SERVICES = ('CMPP', 'CAMSP', 'SESSAD TED', 'SESSAD DYS')
|
33
|
#SERVICES = ('CAMSP',)
|
34
|
#SERVICES = ('CAMSP', 'SESSAD TED', 'SESSAD DYS')
|
35
|
#SERVICES = ('SESSAD DYS',)
|
36
|
|
37
|
QUIET = False
|
38
|
|
39
|
if __name__ == '__main__' :
|
40
|
prefix = '%s-%s.' % (PREFIX, datetime.utcnow())
|
41
|
logger_fn = os.path.join(OUTPUT_DIR, prefix + 'log')
|
42
|
assert not os.path.isfile(logger_fn), 'Analyse log file "%s" already exists' % logger_fn
|
43
|
logger = tempfile.NamedTemporaryFile(suffix='.logtmp',
|
44
|
prefix=prefix, dir=OUTPUT_DIR, delete=False)
|
45
|
|
46
|
logger.write("Script de contôle de Calebasse : %s\n\n" % datetime.utcnow())
|
47
|
|
48
|
need_mail = False
|
49
|
|
50
|
if LOOKUP_NEW:
|
51
|
for service_name in SERVICES:
|
52
|
for date in [(ANALYSE_SDT + timedelta(days=i)).date() for i in range(0, (END_LOOKUP.date()-ANALYSE_SDT.date()).days)]:
|
53
|
"Be sure all acts are created in 2014"
|
54
|
acts = list(Act.objects \
|
55
|
.filter(date=date, patient__service__name=service_name) \
|
56
|
.select_related() \
|
57
|
.prefetch_related('doctors',
|
58
|
'patient__patientcontact',
|
59
|
'actvalidationstate_set__previous_state') \
|
60
|
.order_by('time'))
|
61
|
event_ids = [ a.parent_event_id for a in acts if a.parent_event_id ]
|
62
|
events = EventWithAct.objects.for_today(date) \
|
63
|
.filter(patient__service__name=service_name) \
|
64
|
.exclude(id__in=event_ids)
|
65
|
events = [ event.today_occurrence(date) for event in events ]
|
66
|
acts += [ event.act for event in events if event ]
|
67
|
for act in acts:
|
68
|
if not act.id:
|
69
|
act.save()
|
70
|
|
71
|
logger.write("Traitement des données\n")
|
72
|
logger.write("----------------------\n\n")
|
73
|
invoices = {'CMPP' : {}, 'CAMSP' : {}, 'SESSAD TED' : {}, 'SESSAD DYS' : {}}
|
74
|
for invoice in Invoice.objects.all():
|
75
|
try:
|
76
|
patient = PatientRecord.objects.get(id=invoice.patient_id)
|
77
|
invoices[patient.service.name].setdefault(invoice.invoicing, []).append(invoice)
|
78
|
except:
|
79
|
if invoice.patient_id:
|
80
|
need_mail = True
|
81
|
logger.write("$$$ La facture %d (%d, %d) a pour id patient %d qui n'existe pas\n" % (invoice.id, invoice.batch, invoice.number, invoice.patient_id))
|
82
|
else:
|
83
|
logger.write("La facture %d n'a pas de patient\n" % invoice.id)
|
84
|
if len(invoice.list_dates.split('$')) > invoice.acts.all().count():
|
85
|
logger.write("Cette facture présente des actes manquants\n")
|
86
|
logger.write("Traitement terminé\n\n")
|
87
|
|
88
|
for service_name in SERVICES:
|
89
|
service_str = "Service : %s" % service_name
|
90
|
logger.write('='+'='.join(['' for c in service_str])+"\n")
|
91
|
logger.write(service_str+"\n")
|
92
|
logger.write('='+'='.join(['' for c in service_str])+"\n\n")
|
93
|
|
94
|
acts = Act.objects.filter(date__gte=ANALYSE_SDT.date(), patient__service__name=service_name)
|
95
|
|
96
|
logger.write("Vérifications Event canceled avec des actes already_billed\n")
|
97
|
logger.write("---------------------------------------------------------\n\n")
|
98
|
r_list = []
|
99
|
for id in set([a.parent_event.id for a in Act.objects.filter(parent_event__canceled = True, already_billed = True)]):
|
100
|
e = EventWithAct.objects.get(id=id)
|
101
|
if e.is_recurring():
|
102
|
logger.write("Recurrent : %d\n" % e.id)
|
103
|
r_list.append(id)
|
104
|
elif e.exception_to:
|
105
|
logger.write("Exception %d au recurrent : %d\n" % (e.id, e.exception_to.id))
|
106
|
if not e.exception_to.canceled:
|
107
|
logger.write("\tLe récurrent n'est pas canceled, we can fix easily setting this exception not canceled\n")
|
108
|
else:
|
109
|
logger.write("Simple : %d, we can fix easily setting this exception not canceled\n" % e.id)
|
110
|
|
111
|
logger.write("\n")
|
112
|
|
113
|
logger.write("Vérifications des pointages et vérouillages des actes\n")
|
114
|
logger.write("-----------------------------------------------------\n\n")
|
115
|
logger.write("*** Les 7 valeurs suivantes doivent rester nulles\n")
|
116
|
analyse = [[], [], [], [], [], [], [], []]
|
117
|
for a in acts:
|
118
|
if not a.last_validation_state:
|
119
|
analyse[0].append(a)
|
120
|
need_mail = True
|
121
|
if a.is_billed and (a.is_new() or a.is_absent()):
|
122
|
analyse[1].append(a)
|
123
|
need_mail = True
|
124
|
if a.validation_locked and a.is_new():
|
125
|
analyse[2].append(a)
|
126
|
need_mail = True
|
127
|
if a.is_billed and not a.validation_locked:
|
128
|
analyse[3].append(a)
|
129
|
need_mail = True
|
130
|
if a.is_state('ACT_LOST'):
|
131
|
analyse[4].append(a)
|
132
|
need_mail = True
|
133
|
if a.is_state('VALIDE') and not a.valide:
|
134
|
analyse[5].append(a)
|
135
|
need_mail = True
|
136
|
if not a.parent_event:
|
137
|
analyse[6].append(a)
|
138
|
need_mail = True
|
139
|
if a.invoice_set.all() and not a.already_billed:
|
140
|
analyse[7].append(a)
|
141
|
need_mail = True
|
142
|
|
143
|
logger.write("$$$ Actes sans valeur 'last validation state' : %d\n" % len(analyse[0]))
|
144
|
for a in analyse[0]:
|
145
|
logger.write("\tActe %d : %s\n" % (a.id, a))
|
146
|
logger.write("$$$ Actes facturés mais non pointés en présent : %d\n" % len(analyse[1]))
|
147
|
for a in analyse[1]:
|
148
|
logger.write("\tActe %d : %s\n" % (a.id, a))
|
149
|
logger.write("$$$ Actes vérouillés mais non pointés : %d\n" % len(analyse[2]))
|
150
|
for a in analyse[2]:
|
151
|
logger.write("\tActe %d : %s\n" % (a.id, a))
|
152
|
logger.write("$$$ Actes facturés mais non vérouillés : %d\n" % len(analyse[3]))
|
153
|
for a in analyse[3]:
|
154
|
logger.write("\tActe %d : %s\n" % (a.id, a))
|
155
|
logger.write("$$$ Actes pointés perdus (déprécié) : %d\n" % len(analyse[4]))
|
156
|
for a in analyse[4]:
|
157
|
logger.write("\tActe %d : %s\n" % (a.id, a))
|
158
|
logger.write("$$$ Actes validés mais avec valeur de contrôle non initialisée : %d\n" % len(analyse[5]))
|
159
|
for a in analyse[5]:
|
160
|
logger.write("\tActe %d : %s\n" % (a.id, a))
|
161
|
logger.write("$$$ Actes sans événement parent : %d\n" % len(analyse[6]))
|
162
|
for a in analyse[6]:
|
163
|
logger.write("\tActe %d : %s\n" % (a.id, a))
|
164
|
logger.write("$$$ Actes deja facturés mais already_billed est False : %d\n" % len(analyse[6]))
|
165
|
for a in analyse[7]:
|
166
|
logger.write("\tActe %d : %s\n" % (a.id, a))
|
167
|
logger.write("\n")
|
168
|
|
169
|
|
170
|
logger.write("Vérifications des factures\n")
|
171
|
logger.write("--------------------------\n\n")
|
172
|
|
173
|
for invoicing, invoice_list in invoices[service_name].items():
|
174
|
logger.write("Facturation %d\n" % invoicing.seq_id)
|
175
|
for invoice in invoice_list:
|
176
|
if len(invoice.list_dates.split('$')) > invoice.acts.all().count():
|
177
|
need_mail = True
|
178
|
logger.write("$$$ Facture %d (%d, %d) patient %s avec des actes manquants\n" % (invoice.id, invoice.batch or 0, invoice.number or 0, PatientRecord.objects.get(id=invoice.patient_id)))
|
179
|
acts_dates = [datetime.strptime(d, '%d/%m/%Y').date() for d in invoice.list_dates.split('$')]
|
180
|
if service_name=='CMPP' and len(acts_dates) != len(set(acts_dates)):
|
181
|
print "$$$ Il y a plus d'un acte facturé le meme jour!"
|
182
|
logger.write("$$$ Nombre d'actes manquants %d\n" % (len(acts_dates)-invoice.acts.all().count()))
|
183
|
acts_missing_dates = set(acts_dates) - set([act.date for act in invoice.acts.all()])
|
184
|
for date in acts_missing_dates:
|
185
|
logger.write("$$$ Il manque un acte le %s\n" % date)
|
186
|
else:
|
187
|
#Chech that dates of acts and dates in list_dates match
|
188
|
pass
|
189
|
|
190
|
for act in invoice.acts.all():
|
191
|
if service_name=='CMPP':
|
192
|
if not act.is_billed and not act.is_lost:
|
193
|
logger.write("Facture %d (%d, %d) patient %s avec acte id %d %s à ce jour en refacturation (normal, autres factures %s)\n" % (invoice.id, invoice.batch or 0, invoice.number or 0, PatientRecord.objects.get(id=invoice.patient_id), act.id, act, str([str(i.number) for i in act.invoice_set.all()])))
|
194
|
else:
|
195
|
if not act.is_billed:
|
196
|
need_mail = True
|
197
|
logger.write("$$$ Facture %d (%d, %d) patient %s avec acte id %d %s non is_billed (anormal)\n" % (invoice.id, invoice.batch or 0, invoice.number or 0, PatientRecord.objects.get(id=invoice.patient_id), act.id, act))
|
198
|
|
199
|
logger.write("\n")
|
200
|
|
201
|
logger.write("Vérifications des actes facturés\n")
|
202
|
logger.write("--------------------------------\n\n")
|
203
|
|
204
|
|
205
|
acts_billed = Act.objects.filter(date__gte=ANALYSE_SDT.date(), patient__service__name=service_name, is_billed=True)
|
206
|
for act in acts_billed:
|
207
|
if not act.invoice_set.all():
|
208
|
logger.write("$$$ Acte facturé sans facture id %d : %s\n" % (act.id, act))
|
209
|
need_mail = True
|
210
|
|
211
|
dic = {}
|
212
|
if service_name=='CMPP':
|
213
|
for act in acts_billed:
|
214
|
dic.setdefault(act.patient, []).append(act)
|
215
|
for patient, acts in dic.items():
|
216
|
dates = []
|
217
|
bad_dates = []
|
218
|
for act in acts:
|
219
|
if act.date in dates and not act.date in bad_dates:
|
220
|
bad_dates.append(act.date)
|
221
|
else:
|
222
|
dates.append(act.date)
|
223
|
for date in bad_dates:
|
224
|
need_mail = True
|
225
|
logger.write("$$$ %s a des actes facturés le même jour\n" % patient)
|
226
|
for act in Act.objects.filter(date=date, patient=patient, is_billed=True):
|
227
|
if act.invoice_set.all():
|
228
|
logger.write("Acte %d (factures %s) : %s\n" % (act.id, str([i.number for i in act.invoice_set.all()]), act))
|
229
|
else:
|
230
|
logger.write("Acte %d (sans factures) : %s\n" % (act.id, act))
|
231
|
|
232
|
logger.write("\n")
|
233
|
|
234
|
logger.write("Vérifications des jours de validation\n")
|
235
|
logger.write("-------------------------------------\n\n")
|
236
|
|
237
|
|
238
|
for date in [(ANALYSE_SDT + timedelta(days=i)).date() for i in range(0, (datetime.today().date()-ANALYSE_SDT.date()).days)]:
|
239
|
# Actes non pointés
|
240
|
acts = Act.objects.filter(last_validation_state__state_name='NON_VALIDE', date=date, patient__service__name=service_name)
|
241
|
if acts:
|
242
|
for act in acts:
|
243
|
if act.validation_locked:
|
244
|
need_mail = True
|
245
|
logger.write("$$$ Act %d : %s non validé mais verouillé\n" % (act.id, act))
|
246
|
vms = ValidationMessage.objects.filter(validation_date=date, service__name=service_name)
|
247
|
if not vms:
|
248
|
logger.write("Acte non pointés et aucune opération de validation pour le %s\n" % date)
|
249
|
else:
|
250
|
last = ValidationMessage.objects.filter(validation_date=date, service__name=service_name).latest('when')
|
251
|
if last.what != 'Validation automatique':
|
252
|
logger.write("Jour %s dévérouillé le %s par %s\n" % (date, last.when, last.who))
|
253
|
else:
|
254
|
logger.write("$$$ Jour %s vérouillé le %s par %s\n" % (date, last.when, last.who))
|
255
|
need_mail = True
|
256
|
for act in acts:
|
257
|
logger.write("$$$ Act %d : %s\n" % (act.id, act))
|
258
|
logger.write("$$$ Evenement parent %d créé par %s le %s\n" % (act.parent_event.id, act.parent_event.creator, act.parent_event.create_date))
|
259
|
if act.parent_event.create_date < last.when:
|
260
|
logger.write("-------> Création de l'événement antérieur à la validation\n")
|
261
|
logger.write("\n")
|
262
|
|
263
|
logger.write("\n")
|
264
|
|
265
|
|
266
|
logger.write("Vérifications des actes en double\n")
|
267
|
logger.write("---------------------------------\n\n")
|
268
|
|
269
|
for date in [(ANALYSE_SDT + timedelta(days=i)).date() for i in range(0, (datetime.today().date()-ANALYSE_SDT.date()).days)]:
|
270
|
acts = Act.objects.filter(date=date, patient__service__name=service_name).order_by('time')
|
271
|
if QUIET:
|
272
|
# En double qu'on ne peut traiter.
|
273
|
# On en exclue qu'un seul pour voir si un troisième apparait...
|
274
|
acts = acts.exclude(id__in=(575896, 580500))
|
275
|
acts_p = {}
|
276
|
for a in acts:
|
277
|
acts_p.setdefault(a.patient, []).append(a)
|
278
|
for p, acts in acts_p.items():
|
279
|
if len(acts) > 1:
|
280
|
acts_t = {}
|
281
|
for a in acts:
|
282
|
acts_t.setdefault((a.time, a.act_type), []).append(a)
|
283
|
for k, aa in acts_t.items():
|
284
|
if len(aa) > 1:
|
285
|
t, ty = k
|
286
|
logger.write("$$$ Actes en double pour %s le %s à %s\n" % (p, date, t))
|
287
|
acts_pe = {}
|
288
|
for a in aa:
|
289
|
acts_pe.setdefault(a.parent_event, []).append(a)
|
290
|
if len(aa) == len(acts_pe.keys()):
|
291
|
logger.write("$$$ Les événements parents sont distincts\n")
|
292
|
else:
|
293
|
logger.write("$$$ Il y a des événements parents communs\n")
|
294
|
for a in aa:
|
295
|
logger.write("\t %d : %s\n" % (a.id, a))
|
296
|
logger.write("\t\t %s\n" % a.last_validation_state)
|
297
|
if a.is_billed:
|
298
|
logger.write("\t\t Acte facturé\n")
|
299
|
if a.invoice_set.all():
|
300
|
logger.write("\t\tHistorique de facturation : %s\n" % str([i.number for i in a.invoice_set.all()]))
|
301
|
logger.write("\t\t evenement %d : %s, %s\n" % (a.parent_event.id, a.parent_event.creator, a.parent_event.create_date))
|
302
|
if a.parent_event.description:
|
303
|
logger.write("\t\tCommentaire sur event %s\n" % a.parent_event.description.encode('utf-8'))
|
304
|
if a.parent_event.canceled:
|
305
|
logger.write("\t\tC'est un événement annulé\n")
|
306
|
if a.parent_event.is_recurring():
|
307
|
logger.write("\t\tC'est un événement récurrent\n")
|
308
|
if a.parent_event.exception_to:
|
309
|
logger.write("\t\tC'est une exception à %d\n" % a.parent_event.exception_to.id)
|
310
|
|
311
|
logger.write("\n\n")
|
312
|
|
313
|
|
314
|
if need_mail or ALWAYS_SEND_MAIL:
|
315
|
# send_mail
|
316
|
pass
|
317
|
|
318
|
logger.write("Fin du script de contôle de Calebasse : %s" % datetime.utcnow())
|
319
|
old_fn = logger.name
|
320
|
logger.close()
|
321
|
os.rename(old_fn, logger_fn)
|