Project

General

Profile

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

calebasse / calebasse / facturation / list_acts.py @ 99a62b84

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

    
237
    acts_diagnostic = {}
238
    acts_treatment = {}
239
    acts_losts = {}
240
    acts_losts_missing_policy = {}
241
    acts_losts_missing_birthdate = {}
242
    patient_ids = [p.id for p in acts_billable]
243
    # compute latest hcds using one query
244
    latest_hcd = {}
245
    hcds = CmppHealthCareDiagnostic.objects.filter(patient_id__in=patient_ids).order_by('-start_date').select_related(depth=1).prefetch_related('act_set')
246
    for hcd in hcds:
247
        if hcd.patient not in latest_hcd:
248
            latest_hcd[hcd.patient] = hcd
249
    # compute two latest hcts using one query
250
    latest_hcts = defaultdict(lambda:[])
251
    hcts = CmppHealthCareTreatment.objects.filter(patient_id__in=patient_ids).order_by('-start_date').select_related(depth=1).prefetch_related('act_set')
252
    for hct in hcts:
253
        if hct.patient not in latest_hcts or len(latest_hcts[hct.patient]) < 2:
254
            latest_hcts[hct.patient].append(hct)
255

    
256
    for patient, acts in acts_billable.items():
257
        if not patient.policyholder or \
258
                not patient.policyholder.health_center or \
259
                not patient.policyholder.social_security_id:
260
            acts_losts_missing_policy[patient] = acts
261
            continue
262
        if not patient.birthdate:
263
            acts_losts_missing_birthdate[patient] = acts
264
            continue
265
        # Date de début de la prise en charge ayant servis au dernier acte facturé
266
        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)
267
        last_hc_date = None
268
        if lasts_billed:
269
            last_hc_date = lasts_billed[0].healthcare.start_date
270
        hcd = None
271
        len_acts_cared_diag = 0
272
        try:
273
            hcd = latest_hcd.get(patient)
274
            if not last_hc_date or last_hc_date <= hcd.start_date:
275
                # actes prise en charge par ce hc
276
                len_acts_cared_diag = len(hcd.act_set.all())
277
            else:
278
                # 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.
279
                hcd = None
280
        except:
281
            pass
282
        '''
283
            We take in account the two last treatment healthcare
284
        '''
285
        hcts = latest_hcts.get(patient, [])
286
        # acts are all billable and chronologically ordered
287
        count_hcd = 0
288
        count_hct_1 = 0
289
        count_hct_2 = 0
290
        for act in acts:
291
            cared = False
292
            # If there is overlapping between hc period the more recent must
293
            # be used ! That should not happen with hct but might between
294
            # hcd and hct.
295
            if len(hcts) > 0 and hcts[0] and hcts[0].start_date <= act.date and hcts[0].end_date >= act.date:
296
                if count_hct_2 < hcts[0].get_act_number() - hcts[0].get_nb_acts_cared():
297
                    acts_treatment.setdefault(patient, []).append((act, hcts[0]))
298
                    count_hct_2 = count_hct_2 + 1
299
                    cared = True
300
            if not cared and len(hcts) > 1 and hcts[1] and hcts[1].start_date <= act.date and hcts[1].end_date >= act.date:
301
                # Ce qui seraient prise en charge
302
                # ne doit pas dépasser la limite de prise en charge du hc
303
                if count_hct_1 < hcts[1].get_act_number() - hcts[1].get_nb_acts_cared():
304
                    acts_treatment.setdefault(patient, []).append((act, hcts[1]))
305
                    count_hct_1 = count_hct_1 + 1
306
                    cared = True
307
            if not cared and hcd and hcd.start_date <= act.date and \
308
                    (not hcd.end_date or hcd.end_date >= act.date):
309
                # Ce qui seraient prise en charge
310
                nb_acts_cared = len_acts_cared_diag + count_hcd
311
                # Ne doit pas dépasser la limite de prise en charge du hc
312
                if nb_acts_cared < hcd.get_act_number() :
313
                    acts_diagnostic.setdefault(patient, []).append((act, hcd))
314
                    count_hcd = count_hcd + 1
315
                    cared = True
316
            if not cared:
317
                acts_losts.setdefault(patient, []).append(act)
318
    return (acts_not_locked, days_not_locked, acts_not_valide,
319
        acts_not_billable, acts_pause, acts_diagnostic,
320
        acts_treatment, acts_losts, acts_losts_missing_policy,
321
        acts_losts_missing_birthdate)
322

    
323
def list_acts_for_billing_CMPP_per_patient(patient, end_day, service, acts=None):
324

    
325
    acts_not_locked, days_not_locked, acts_not_valide, \
326
        acts_not_billable, acts_pause, acts_billable = \
327
            list_acts_for_billing_first_round(end_day, service, acts=acts, patient=patient)
328

    
329
    acts_per_hc = {}
330
    acts_losts = []
331
    if len(acts_billable.keys()) > 1:
332
        raise "Should not find more than one patient"
333
    elif len(acts_billable.keys()) == 1:
334
        # Date de début de la prise en charge ayant servis au dernier acte facturé
335
        lasts_billed = Act.objects.filter(patient=patient, is_billed = True, healthcare__isnull=False).order_by('-date')
336
        last_hc_date = None
337
        if lasts_billed:
338
            last_hc_date = lasts_billed[0].healthcare.start_date
339
        patient, acts = acts_billable.items()[0]
340
        hcd = None
341
        len_acts_cared_diag = 0
342
        try:
343
            hcd = CmppHealthCareDiagnostic.objects.\
344
                filter(patient=patient).latest('start_date')
345
            if not last_hc_date or last_hc_date <= hcd.start_date:
346
                # actes prise en charge par ce hc
347
                len_acts_cared_diag = len(hcd.act_set.all())
348
            else:
349
                # 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.
350
                hcd = None
351
        except:
352
            pass
353
        '''
354
            We take in account the two last treatment healthcare
355
        '''
356
        hcts = None
357
        len_acts_cared_trait = 0
358
        try:
359
            hcts = CmppHealthCareTreatment.objects.\
360
                filter(patient=patient).order_by('-start_date')
361
        except:
362
            pass
363
        # acts are all billable and chronologically ordered
364
        count_hcd = 0
365
        count_hct_1 = 0
366
        count_hct_2 = 0
367
        if hcd:
368
            acts_per_hc[hcd] = []
369
        if len(hcts) > 0:
370
            acts_per_hc[hcts[0]] = []
371
        if len(hcts) > 1:
372
            acts_per_hc[hcts[1]] = []
373
        for act in acts:
374
            cared = False
375
            # If there is overlapping between hc period the more recent must
376
            # be used ! That should not happen with hct but might between
377
            # hcd and hct.
378
            if len(hcts) > 0 and hcts[0] and hcts[0].start_date <= act.date and hcts[0].end_date >= act.date:
379
                if count_hct_2 < hcts[0].get_act_number() - hcts[0].get_nb_acts_cared():
380
                    acts_per_hc[hcts[0]].append(act)
381
                    count_hct_2 = count_hct_2 + 1
382
                    cared = True
383
            if not cared and len(hcts) > 1 and hcts[1] and hcts[1].start_date <= act.date and hcts[1].end_date >= act.date:
384
                # Ce qui seraient prise en charge
385
                # ne doit pas dépasser la limite de prise en charge du hc
386
                if count_hct_1 < hcts[1].get_act_number() - hcts[1].get_nb_acts_cared():
387
                    acts_per_hc[hcts[1]].append(act)
388
                    count_hct_1 = count_hct_1 + 1
389
                    cared = True
390
            if not cared and hcd and hcd.start_date <= act.date and \
391
                    (not hcd.end_date or hcd.end_date >= act.date):
392
                # Ce qui seraient prise en charge
393
                nb_acts_cared = len_acts_cared_diag + count_hcd
394
                # Ne doit pas dépasser la limite de prise en charge du hc
395
                if nb_acts_cared < hcd.get_act_number() :
396
                    acts_per_hc[hcd].append(act)
397
                    count_hcd = count_hcd + 1
398
                    cared = True
399
            if not cared:
400
                acts_losts.append(act)
401
    if len(acts_pause.keys()) == 1:
402
        acts_pause = acts_pause.values()[0]
403
    else:
404
        acts_pause = []
405
    return (acts_not_locked, days_not_locked, acts_not_valide,
406
        acts_not_billable, acts_pause, acts_per_hc, acts_losts)
(8-8/16)