Project

General

Profile

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

calebasse / calebasse / facturation / list_acts.py @ 523c5aaf

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:
308
                # Ce qui seraient prise en charge
309
                nb_acts_cared = len_acts_cared_diag + count_hcd
310
                # Ne doit pas dépasser la limite de prise en charge du hc
311
                if nb_acts_cared < hcd.get_act_number() :
312
                    acts_diagnostic.setdefault(patient, []).append((act, hcd))
313
                    count_hcd = count_hcd + 1
314
                    cared = True
315
            if not cared:
316
                acts_losts.setdefault(patient, []).append(act)
317
    return (acts_not_locked, days_not_locked, acts_not_valide,
318
        acts_not_billable, acts_pause, acts_diagnostic,
319
        acts_treatment, acts_losts, acts_losts_missing_policy,
320
        acts_losts_missing_birthdate)
321

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

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

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