Project

General

Profile

Download (18.7 KB) Statistics
| Branch: | Tag: | Revision:

calebasse / calebasse / facturation / list_acts.py @ edf91555

1
# -*- coding: utf-8 -*-
2
from datetime import datetime
3
from collections import defaultdict
4

    
5
from django.db.models import Q
6

    
7
from calebasse.actes.models import Act
8
from calebasse.dossiers.models import (CmppHealthCareDiagnostic,
9
    CmppHealthCareTreatment)
10

    
11

    
12
def list_acts_for_billing_first_round(end_day, service, start_day=None, acts=None, patient=None):
13
    """Used to sort acts and extract acts billable before specific service
14
        requirements.
15

    
16
    At first, all acts not billed are listed per patient.
17
    Then acts are sorted.
18
    Each sorting split the acts in two sets, one set for rejected acts,
19
        on set for selected sets.
20
    First sort rejects acts days where all acts are not locked:
21
        acts_not_locked
22
    Second sort rejects acts not in state 'VALIDE':
23
        acts_not_valide
24
    Third sort rejects acts not billable:
25
        acts_not_billable
26
    Acts billable in :
27
        acts_billable
28

    
29
    acts = acts_not_locked + \
30
        acts_not_valide + \
31
            acts_not_billable + \
32
                acts_billable
33

    
34
    :param end_day: formatted date that gives the last day when acts are taken
35
        in account.
36
    :type end_day: datetime
37
    :param service: service in which acts are dealt with.
38
    :type service: calebasse.ressources.Service
39

    
40
    :returns: a list of dictionnaries where patients are the keys and values
41
        are lists of acts. The second element of this list in not a dict but
42
        a list of the days where are all days are not locked.
43
    :rtype: list
44
    """
45

    
46
    from calebasse.actes.models import Act
47
    if isinstance(end_day, datetime):
48
        end_day = end_day.date()
49
    if start_day and isinstance(start_day, datetime):
50
        start_day = start_day.date()
51

    
52
    acts = Act.objects.filter(validation_locked=False,
53
        patient__service=service, date__lte=end_day)
54
    if start_day:
55
        acts = acts.filter(date__gte=start_day)
56
    days_not_locked = sorted(set(acts.values_list('date', flat=True)))
57

    
58
    acts = None
59
    if patient:
60
        acts = Act.objects.filter(patient=patient, validation_locked=True,
61
            is_billed=False, valide=True, is_lost=False,
62
            patient__service=service, date__lte=end_day)
63
    else:
64
        acts = Act.objects.filter(validation_locked=True,
65
            is_billed=False, valide=True, is_lost=False,
66
            patient__service=service, date__lte=end_day)
67
    if start_day:
68
        acts = acts.filter(date__gte=start_day)
69
    acts = acts.exclude(date__in=days_not_locked)
70
    acts = acts.order_by('date')
71

    
72
    # deprecated
73
    acts_not_locked = {}
74
    acts_not_valide = {}
75

    
76
    acts_pause = {}
77
    acts_not_billable = {}
78
    acts_billable = {}
79

    
80

    
81
    pause_query = Q(pause=True)
82
    billable_query = Q(act_type__billable=True, switch_billable=False) | \
83
            Q(act_type__billable=False, switch_billable=True)
84

    
85
    paused_acts = acts.filter(pause_query).select_related('patient', 'act_type')
86
    not_billable_acts = acts.filter(~pause_query & ~billable_query).select_related('patient', 'act_type')
87
    billable_acts = acts.filter(~pause_query & billable_query)
88
    billable_acts = billable_acts.select_related('act_type', 'patient__policyholder__health_center').prefetch_related('patient__act_set', 'patient__act_set__healthcare')
89

    
90
    for act in paused_acts:
91
        acts_pause.setdefault(act.patient, []).append(act)
92

    
93
    for act in not_billable_acts:
94
        acts_not_billable.setdefault(act.patient, []).append(act)
95

    
96
    for act in billable_acts:
97
        acts_billable.setdefault(act.patient, []).append(act)
98
    return (acts_not_locked, days_not_locked, acts_not_valide,
99
        acts_not_billable, acts_pause, acts_billable)
100

    
101

    
102
def list_acts_for_billing_CAMSP(start_day, end_day, service, acts=None):
103
    """Used to sort acts billable by specific service requirements.
104

    
105
    For the CAMSP, only the state of the patient record 'CAMSP_STATE_SUIVI'
106
        at the date of the act determine if the acte is billable.
107

    
108
    acts = acts_not_locked + \
109
        acts_not_valide + \
110
            acts_not_billable + \
111
                acts_bad_state + \
112
                    acts_accepted
113

    
114
    :param end_day: formatted date that gives the last day when acts are taken
115
        in account.
116
    :type end_day: datetime
117
    :param service: service in which acts are dealt with.
118
    :type service: calebasse.ressources.Service
119

    
120
    :returns: a list of dictionnaries where patients are the keys and values
121
        are lists of acts. The second element of this list in not a dict but
122
        a list of the days where are all days are not locked.
123
    :rtype: list
124
    """
125

    
126
    acts_not_locked, days_not_locked, acts_not_valide, \
127
        acts_not_billable, acts_pause, acts_billable = \
128
            list_acts_for_billing_first_round(end_day, service,
129
                start_day, acts=acts)
130
    acts_bad_state = {}
131
    acts_accepted = {}
132
    patients_missing_policy = []
133
    for patient, acts in acts_billable.iteritems():
134
        for act in acts:
135
            if patient.was_in_state_at_day(act.date, 'SUIVI'):
136
                acts_accepted.setdefault(act.patient, []).append(act)
137
            else:
138
                acts_bad_state.setdefault(act.patient, []).\
139
                    append((act, 'NOT_ACCOUNTABLE_STATE'))
140
    for patient in acts_accepted.keys():
141
        if not patient.policyholder or \
142
                not patient.policyholder.health_center or \
143
                not patient.policyholder.management_code or \
144
                not patient.policyholder.social_security_id:
145
            patients_missing_policy.append(patient)
146
    return (acts_not_locked, days_not_locked, acts_not_valide,
147
        acts_not_billable, acts_pause, acts_bad_state,
148
        acts_accepted, patients_missing_policy)
149

    
150

    
151
def list_acts_for_billing_SESSAD(start_day, end_day, service, acts=None):
152
    """Used to sort acts billable by specific service requirements.
153

    
154
    For the SESSAD, acts are billable if the state of the patient record at
155
        the date of the act is 'SESSAD_STATE_TRAITEMENT' and there was also a
156
        valid notification at that date.
157

    
158
    acts = acts_not_locked + \
159
        acts_not_valide + \
160
            acts_not_billable + \
161
                acts_bad_state + \
162
                    acts_missing_valid_notification + \
163
                        acts_accepted
164

    
165
    :param end_day: formatted date that gives the last day when acts are taken
166
        in account.
167
    :type end_day: datetime
168
    :param service: service in which acts are dealt with.
169
    :type service: calebasse.ressources.Service
170

    
171
    :returns: a list of dictionnaries where patients are the keys and values
172
        are lists of acts. The second element of this list in not a dict but
173
        a list of the days where are all days are not locked.
174
    :rtype: list
175
    """
176

    
177
    acts_not_locked, days_not_locked, acts_not_valide, \
178
        acts_not_billable, acts_pause, acts_billable = \
179
            list_acts_for_billing_first_round(end_day, service,
180
                start_day=start_day, acts=acts)
181
    acts_bad_state = {}
182
    acts_missing_valid_notification = {}
183
    acts_accepted = {}
184
    patients_missing_policy = []
185
    for patient, acts in acts_billable.iteritems():
186
        for act in acts:
187
            if patient.was_in_state_at_day(act.date,
188
                    'TRAITEMENT'):
189
                if not act.was_covered_by_notification():
190
                    acts_missing_valid_notification.\
191
                        setdefault(act.patient, []).append(act)
192
                else:
193
                    acts_accepted.setdefault(act.patient, []).append(act)
194
            else:
195
                acts_bad_state.setdefault(act.patient, []).\
196
                    append((act, 'NOT_ACCOUNTABLE_STATE'))
197
    for patient in acts_accepted.keys():
198
        if not patient.policyholder or \
199
                not patient.policyholder.health_center or \
200
                not patient.policyholder.management_code or \
201
                not patient.policyholder.social_security_id:
202
            patients_missing_policy.append(patient)
203
    return (acts_not_locked, days_not_locked, acts_not_valide,
204
        acts_not_billable, acts_pause, acts_bad_state,
205
        acts_missing_valid_notification, acts_accepted,
206
        patients_missing_policy)
207

    
208

    
209
def list_acts_for_billing_CMPP(end_day, service, acts=None):
210
    """Used to sort acts billable by specific service requirements.
211

    
212
    For the CMPP, acts are billable if
213

    
214
    acts = acts_not_locked + \
215
        acts_not_valide + \
216
            acts_not_billable + \
217
                acts_diagnostic + \
218
                    acts_treatment + \
219
                        acts_losts
220

    
221
    :param end_day: formatted date that gives the last day when acts are taken
222
        in account.
223
    :type end_day: datetime
224
    :param service: service in which acts are dealt with.
225
    :type service: calebasse.ressources.Service
226

    
227
    :returns: a list of dictionnaries where patients are the keys and values
228
        are lists of acts. The second element of this list in not a dict but
229
        a list of the days where are all days are not locked.
230
    :rtype: list
231
    """
232

    
233
    acts_not_locked, days_not_locked, acts_not_valide, \
234
        acts_not_billable, acts_pause, acts_billable = \
235
            list_acts_for_billing_first_round(end_day, service, acts=acts)
236
    acts_diagnostic = {}
237
    acts_treatment = {}
238
    acts_losts = {}
239
    for patient, acts in acts_billable.items():
240
        for act in acts:
241
            cared, hc = act.is_act_covered_by_diagnostic_healthcare()
242
            if cared:
243
                if act.patient in acts_diagnostic:
244
                    acts_diagnostic[act.patient]. \
245
                        append((act, hc))
246
                else:
247
                    acts_diagnostic[act.patient] = [(act, hc)]
248
            else:
249
                cared, hc = act.is_act_covered_by_treatment_healthcare()
250
                if cared:
251
                    if act.patient in acts_treatment:
252
                        acts_treatment[act.patient]. \
253
                            append((act, hc))
254
                    else:
255
                        acts_treatment[act.patient] = [(act, hc)]
256
                else:
257
                    if act.patient in acts_losts:
258
                        acts_losts[act.patient]. \
259
                            append(act)
260
                    else:
261
                        acts_losts[act.patient] = [act]
262
    return (acts_not_locked, days_not_locked, acts_not_valide,
263
        acts_not_billable, acts_pause, acts_diagnostic,
264
        acts_treatment, acts_losts)
265

    
266
def list_acts_for_billing_CMPP_2(end_day, service, acts=None):
267
    """Used to sort acts billable by specific service requirements.
268

    
269
    For the CMPP, acts are billable if
270

    
271
    acts = acts_not_locked + \
272
        acts_not_valide + \
273
            acts_not_billable + \
274
                acts_diagnostic + \
275
                    acts_treatment + \
276
                        acts_losts
277

    
278
    :param end_day: formatted date that gives the last day when acts are taken
279
        in account.
280
    :type end_day: datetime
281
    :param service: service in which acts are dealt with.
282
    :type service: calebasse.ressources.Service
283

    
284
    :returns: a list of dictionnaries where patients are the keys and values
285
        are lists of acts. The second element of this list in not a dict but
286
        a list of the days where are all days are not locked.
287
    :rtype: list
288
    """
289

    
290
    acts_not_locked, days_not_locked, acts_not_valide, \
291
        acts_not_billable, acts_pause, acts_billable = \
292
            list_acts_for_billing_first_round(end_day, service, acts=acts)
293

    
294
    acts_diagnostic = {}
295
    acts_treatment = {}
296
    acts_losts = {}
297
    acts_losts_missing_policy = {}
298
    patient_ids = [p.id for p in acts_billable]
299
    # compute latest hcds using one query
300
    latest_hcd = {}
301
    hcds = CmppHealthCareDiagnostic.objects.filter(patient_id__in=patient_ids).order_by('-start_date').select_related(depth=1).prefetch_related('act_set')
302
    for hcd in hcds:
303
        if hcd.patient not in latest_hcd:
304
            latest_hcd[hcd.patient] = hcd
305
    # compute two latest hcts using one query
306
    latest_hcts = defaultdict(lambda:[])
307
    hcts = CmppHealthCareTreatment.objects.filter(patient_id__in=patient_ids).order_by('-start_date').select_related(depth=1).prefetch_related('act_set')
308
    for hct in hcts:
309
        if hct.patient not in latest_hcts or len(latest_hcts[hct.patient]) < 2:
310
            latest_hcts[hct.patient].append(hct)
311

    
312
    for patient, acts in acts_billable.items():
313
        if not patient.policyholder or \
314
                not patient.policyholder.health_center or \
315
                not patient.policyholder.social_security_id:
316
            acts_losts_missing_policy[patient] = acts
317
            continue
318
        # Date de début de la prise en charge ayant servis au dernier acte facturé
319
        lasts_billed = sorted(filter(lambda a: a.is_billed and a.healthcare is not None, patient.act_set.all()), key=lambda a: a.date, reverse=True)
320
        last_hc_date = None
321
        if lasts_billed:
322
            last_hc_date = lasts_billed[0].healthcare.start_date
323
        hcd = None
324
        len_acts_cared_diag = 0
325
        try:
326
            hcd = latest_hcd.get(patient)
327
            if not last_hc_date or last_hc_date <= hcd.start_date:
328
                # actes prise en charge par ce hc
329
                len_acts_cared_diag = len(hcd.act_set.all())
330
            else:
331
                # Comme un PC diag n'a pas de date de fin, on considère qu'elle ne sert plus si un acte a été couvert par une prise en charge plus récente.
332
                hcd = None
333
        except:
334
            pass
335
        '''
336
            We take in account the two last treatment healthcare
337
        '''
338
        hcts = latest_hcts.get(patient, [])
339
        # acts are all billable and chronologically ordered
340
        count_hcd = 0
341
        count_hct_1 = 0
342
        count_hct_2 = 0
343
        for act in acts:
344
            cared = False
345
            if hcd and hcd.start_date <= act.date:
346
                # Ce qui seraient prise en charge
347
                nb_acts_cared = len_acts_cared_diag + count_hcd
348
                # Ne doit pas dépasser la limite de prise en charge du hc
349
                if nb_acts_cared < hcd.get_act_number() :
350
                    acts_diagnostic.setdefault(patient, []).append((act, hcd))
351
                    count_hcd = count_hcd + 1
352
                    cared = True
353
            # The one before the last may be not full.
354
            if not cared and len(hcts) > 1 and hcts[1] and hcts[1].start_date <= act.date and hcts[1].end_date >= act.date:
355
                # Ce qui seraient prise en charge
356
                # ne doit pas dépasser la limite de prise en charge du hc
357
                if count_hct_1 < hcts[1].get_act_number() - hcts[1].get_nb_acts_cared():
358
                    acts_treatment.setdefault(patient, []).append((act, hcts[1]))
359
                    count_hct_1 = count_hct_1 + 1
360
                    cared = True
361
            if not cared and len(hcts) > 0 and hcts[0] and hcts[0].start_date <= act.date and hcts[0].end_date >= act.date:
362
                if count_hct_2 < hcts[0].get_act_number() - hcts[0].get_nb_acts_cared():
363
                    acts_treatment.setdefault(patient, []).append((act, hcts[0]))
364
                    count_hct_2 = count_hct_2 + 1
365
                    cared = True
366
            if not cared:
367
                acts_losts.setdefault(patient, []).append(act)
368
    return (acts_not_locked, days_not_locked, acts_not_valide,
369
        acts_not_billable, acts_pause, acts_diagnostic,
370
        acts_treatment, acts_losts, acts_losts_missing_policy)
371

    
372
def list_acts_for_billing_CMPP_2_per_patient(patient, end_day, service, acts=None):
373

    
374
    acts_not_locked, days_not_locked, acts_not_valide, \
375
        acts_not_billable, acts_pause, acts_billable = \
376
            list_acts_for_billing_first_round(end_day, service, acts=acts, patient=patient)
377

    
378
    acts_per_hc = {}
379
    acts_losts = []
380
    if len(acts_billable.keys()) > 1:
381
        raise "Should not find more than one patient"
382
    elif len(acts_billable.keys()) == 1:
383
        # Date de début de la prise en charge ayant servis au dernier acte facturé
384
        lasts_billed = Act.objects.filter(patient=patient, is_billed = True, healthcare__isnull=False).order_by('-date')
385
        last_hc_date = None
386
        if lasts_billed:
387
            last_hc_date = lasts_billed[0].healthcare.start_date
388
        patient, acts = acts_billable.items()[0]
389
        hcd = None
390
        len_acts_cared_diag = 0
391
        try:
392
            hcd = CmppHealthCareDiagnostic.objects.\
393
                filter(patient=patient).latest('start_date')
394
            if not last_hc_date or last_hc_date <= hcd.start_date:
395
                # actes prise en charge par ce hc
396
                len_acts_cared_diag = len(hcd.act_set.all())
397
            else:
398
                # Comme un PC diag n'a pas de date de fin, on considère qu'elle ne sert plus si un acte a été couvert par une prise en charge plus récente.
399
                hcd = None
400
        except:
401
            pass
402
        '''
403
            We take in account the two last treatment healthcare
404
        '''
405
        hcts = None
406
        len_acts_cared_trait = 0
407
        try:
408
            hcts = CmppHealthCareTreatment.objects.\
409
                filter(patient=patient).order_by('-start_date')
410
        except:
411
            pass
412
        # acts are all billable and chronologically ordered
413
        count_hcd = 0
414
        count_hct_1 = 0
415
        count_hct_2 = 0
416
        if hcd:
417
            acts_per_hc[hcd] = []
418
        if len(hcts) > 0:
419
            acts_per_hc[hcts[0]] = []
420
        if len(hcts) > 1:
421
            acts_per_hc[hcts[1]] = []
422
        for act in acts:
423
            cared = False
424
            if hcd and hcd.start_date <= act.date:
425
                # Ce qui seraient prise en charge
426
                nb_acts_cared = len_acts_cared_diag + count_hcd
427
                # Ne doit pas dépasser la limite de prise en charge du hc
428
                if nb_acts_cared < hcd.get_act_number() :
429
                    acts_per_hc[hcd].append(act)
430
                    count_hcd = count_hcd + 1
431
                    cared = True
432
            # The one before the last may be not full.
433
            if not cared and len(hcts) > 1 and hcts[1] and hcts[1].start_date <= act.date and hcts[1].end_date >= act.date:
434
                # Ce qui seraient prise en charge
435
                # ne doit pas dépasser la limite de prise en charge du hc
436
                if count_hct_1 < hcts[1].get_act_number() - hcts[1].get_nb_acts_cared():
437
                    acts_per_hc[hcts[1]].append(act)
438
                    count_hct_1 = count_hct_1 + 1
439
                    cared = True
440
            if not cared and len(hcts) > 0 and hcts[0] and hcts[0].start_date <= act.date and hcts[0].end_date >= act.date:
441
                if count_hct_2 < hcts[0].get_act_number() - hcts[0].get_nb_acts_cared():
442
                    print
443
                    acts_per_hc[hcts[0]].append(act)
444
                    count_hct_2 = count_hct_2 + 1
445
                    cared = True
446
            if not cared:
447
                acts_losts.append(act)
448
    return (acts_not_locked, days_not_locked, acts_not_valide,
449
        acts_not_billable, acts_pause, acts_per_hc, acts_losts)
(8-8/14)