Project

General

Profile

Download (17.1 KB) Statistics
| Branch: | Tag: | Revision:
731000d7 Mikaël Ates
# -*- coding: utf-8 -*-
16e28edd Benjamin Dauvergne
from datetime import datetime
76090589 Benjamin Dauvergne
from collections import defaultdict

from django.db.models import Q
731000d7 Mikaël Ates
6fd159cb Mikaël Ates
from calebasse.actes.models import Act
9d3ab5f7 Mikaël Ates
from calebasse.dossiers.models import (CmppHealthCareDiagnostic,
CmppHealthCareTreatment)
731000d7 Mikaël Ates

6fd159cb Mikaël Ates
def list_acts_for_billing_first_round(end_day, service, start_day=None, acts=None, patient=None):
731000d7 Mikaël Ates
"""Used to sort acts and extract acts billable before specific service
requirements.

At first, all acts not billed are listed per patient.
Then acts are sorted.
Each sorting split the acts in two sets, one set for rejected acts,
on set for selected sets.
First sort rejects acts days where all acts are not locked:
acts_not_locked
Second sort rejects acts not in state 'VALIDE':
acts_not_valide
Third sort rejects acts not billable:
acts_not_billable
Acts billable in :
acts_billable

acts = acts_not_locked + \
acts_not_valide + \
acts_not_billable + \
acts_billable

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

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

191226bf Benjamin Dauvergne
from calebasse.actes.models import Act
b2398844 Benjamin Dauvergne
if isinstance(end_day, datetime):
end_day = end_day.date()
51f6d525 Mikaël Ates
if start_day and isinstance(start_day, datetime):
start_day = start_day.date()

acts = Act.objects.filter(validation_locked=False,
patient__service=service, date__lte=end_day)
731000d7 Mikaël Ates
if start_day:
f8d74927 Mikaël Ates
acts = acts.filter(date__gte=start_day)
5aa71431 Mikaël Ates
days_not_locked = sorted(set(acts.values_list('date', flat=True)))

6fd159cb Mikaël Ates
acts = None
if patient:
acts = Act.objects.filter(patient=patient, validation_locked=True,
is_billed=False, valide=True, is_lost=False,
patient__service=service, date__lte=end_day)
else:
acts = Act.objects.filter(validation_locked=True,
is_billed=False, valide=True, is_lost=False,
patient__service=service, date__lte=end_day)
5aa71431 Mikaël Ates
if start_day:
acts = acts.filter(date__gte=start_day)
51f6d525 Mikaël Ates
acts = acts.exclude(date__in=days_not_locked)
5aa71431 Mikaël Ates
acts = acts.order_by('date')
51f6d525 Mikaël Ates
# deprecated
731000d7 Mikaël Ates
acts_not_locked = {}
acts_not_valide = {}
51f6d525 Mikaël Ates
bc7e290e Mikaël Ates
acts_pause = {}
51f6d525 Mikaël Ates
acts_not_billable = {}
731000d7 Mikaël Ates
acts_billable = {}
76090589 Benjamin Dauvergne

pause_query = Q(pause=True)
billable_query = Q(act_type__billable=True, switch_billable=False) | \
Q(act_type__billable=False, switch_billable=True)

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

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

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

for act in billable_acts:
acts_billable.setdefault(act.patient, []).append(act)
731000d7 Mikaël Ates
return (acts_not_locked, days_not_locked, acts_not_valide,
bc7e290e Mikaël Ates
acts_not_billable, acts_pause, acts_billable)
731000d7 Mikaël Ates

3aca239f Benjamin Dauvergne
def list_acts_for_billing_CAMSP(start_day, end_day, service, acts=None):
731000d7 Mikaël Ates
"""Used to sort acts billable by specific service requirements.

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

acts = acts_not_locked + \
acts_not_valide + \
acts_not_billable + \
acts_bad_state + \
acts_accepted

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

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

acts_not_locked, days_not_locked, acts_not_valide, \
bc7e290e Mikaël Ates
acts_not_billable, acts_pause, acts_billable = \
731000d7 Mikaël Ates
list_acts_for_billing_first_round(end_day, service,
3aca239f Benjamin Dauvergne
start_day, acts=acts)
731000d7 Mikaël Ates
acts_bad_state = {}
acts_accepted = {}
edf91555 Mikaël Ates
patients_missing_policy = []
for patient, acts in acts_billable.iteritems():
731000d7 Mikaël Ates
for act in acts:
8ef57f1f Mikaël Ates
if patient.was_in_state_at_day(act.date, 'SUIVI'):
edf91555 Mikaël Ates
acts_accepted.setdefault(act.patient, []).append(act)
731000d7 Mikaël Ates
else:
edf91555 Mikaël Ates
acts_bad_state.setdefault(act.patient, []).\
append((act, 'NOT_ACCOUNTABLE_STATE'))
for patient in acts_accepted.keys():
if not patient.policyholder or \
not patient.policyholder.health_center or \
not patient.policyholder.management_code or \
not patient.policyholder.social_security_id:
patients_missing_policy.append(patient)
731000d7 Mikaël Ates
return (acts_not_locked, days_not_locked, acts_not_valide,
bc7e290e Mikaël Ates
acts_not_billable, acts_pause, acts_bad_state,
edf91555 Mikaël Ates
acts_accepted, patients_missing_policy)
82feb7c2 Mikaël Ates

3aca239f Benjamin Dauvergne
def list_acts_for_billing_SESSAD(start_day, end_day, service, acts=None):
82feb7c2 Mikaël Ates
"""Used to sort acts billable by specific service requirements.

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

acts = acts_not_locked + \
acts_not_valide + \
acts_not_billable + \
acts_bad_state + \
acts_missing_valid_notification + \
acts_accepted

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

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

acts_not_locked, days_not_locked, acts_not_valide, \
bc7e290e Mikaël Ates
acts_not_billable, acts_pause, acts_billable = \
82feb7c2 Mikaël Ates
list_acts_for_billing_first_round(end_day, service,
3aca239f Benjamin Dauvergne
start_day=start_day, acts=acts)
82feb7c2 Mikaël Ates
acts_bad_state = {}
acts_missing_valid_notification = {}
acts_accepted = {}
edf91555 Mikaël Ates
patients_missing_policy = []
for patient, acts in acts_billable.iteritems():
82feb7c2 Mikaël Ates
for act in acts:
if patient.was_in_state_at_day(act.date,
8ef57f1f Mikaël Ates
'TRAITEMENT'):
82feb7c2 Mikaël Ates
if not act.was_covered_by_notification():
edf91555 Mikaël Ates
acts_missing_valid_notification.\
setdefault(act.patient, []).append(act)
82feb7c2 Mikaël Ates
else:
edf91555 Mikaël Ates
acts_accepted.setdefault(act.patient, []).append(act)
82feb7c2 Mikaël Ates
else:
edf91555 Mikaël Ates
acts_bad_state.setdefault(act.patient, []).\
append((act, 'NOT_ACCOUNTABLE_STATE'))
for patient in acts_accepted.keys():
if not patient.policyholder or \
not patient.policyholder.health_center or \
not patient.policyholder.management_code or \
not patient.policyholder.social_security_id:
patients_missing_policy.append(patient)
82feb7c2 Mikaël Ates
return (acts_not_locked, days_not_locked, acts_not_valide,
bc7e290e Mikaël Ates
acts_not_billable, acts_pause, acts_bad_state,
edf91555 Mikaël Ates
acts_missing_valid_notification, acts_accepted,
patients_missing_policy)
f9afb714 Mikaël Ates

523c5aaf Mikaël Ates
def list_acts_for_billing_CMPP(end_day, service, acts=None):
9d3ab5f7 Mikaël Ates
"""Used to sort acts billable by specific service requirements.

For the CMPP, acts are billable if

acts = acts_not_locked + \
acts_not_valide + \
acts_not_billable + \
acts_diagnostic + \
acts_treatment + \
acts_losts

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

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

acts_not_locked, days_not_locked, acts_not_valide, \
bc7e290e Mikaël Ates
acts_not_billable, acts_pause, acts_billable = \
9d3ab5f7 Mikaël Ates
list_acts_for_billing_first_round(end_day, service, acts=acts)

acts_diagnostic = {}
acts_treatment = {}
acts_losts = {}
e32de143 Mikaël Ates
acts_losts_missing_policy = {}
36f1b357 Mikaël Ates
acts_losts_missing_birthdate = {}
76090589 Benjamin Dauvergne
patient_ids = [p.id for p in acts_billable]
# compute latest hcds using one query
latest_hcd = {}
hcds = CmppHealthCareDiagnostic.objects.filter(patient_id__in=patient_ids).order_by('-start_date').select_related(depth=1).prefetch_related('act_set')
for hcd in hcds:
if hcd.patient not in latest_hcd:
latest_hcd[hcd.patient] = hcd
# compute two latest hcts using one query
latest_hcts = defaultdict(lambda:[])
hcts = CmppHealthCareTreatment.objects.filter(patient_id__in=patient_ids).order_by('-start_date').select_related(depth=1).prefetch_related('act_set')
for hct in hcts:
if hct.patient not in latest_hcts or len(latest_hcts[hct.patient]) < 2:
latest_hcts[hct.patient].append(hct)

9d3ab5f7 Mikaël Ates
for patient, acts in acts_billable.items():
e32de143 Mikaël Ates
if not patient.policyholder or \
not patient.policyholder.health_center or \
not patient.policyholder.social_security_id:
acts_losts_missing_policy[patient] = acts
continue
36f1b357 Mikaël Ates
if not patient.birthdate:
acts_losts_missing_birthdate[patient] = acts
continue
6fd159cb Mikaël Ates
# Date de début de la prise en charge ayant servis au dernier acte facturé
76090589 Benjamin Dauvergne
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)
6fd159cb Mikaël Ates
last_hc_date = None
if lasts_billed:
last_hc_date = lasts_billed[0].healthcare.start_date
9d3ab5f7 Mikaël Ates
hcd = None
len_acts_cared_diag = 0
try:
76090589 Benjamin Dauvergne
hcd = latest_hcd.get(patient)
6fd159cb Mikaël Ates
if not last_hc_date or last_hc_date <= hcd.start_date:
# actes prise en charge par ce hc
len_acts_cared_diag = len(hcd.act_set.all())
else:
# 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.
hcd = None
9d3ab5f7 Mikaël Ates
except:
pass
9a64c930 Mikaël Ates
'''
We take in account the two last treatment healthcare
'''
76090589 Benjamin Dauvergne
hcts = latest_hcts.get(patient, [])
9d3ab5f7 Mikaël Ates
# acts are all billable and chronologically ordered
count_hcd = 0
9a64c930 Mikaël Ates
count_hct_1 = 0
count_hct_2 = 0
9d3ab5f7 Mikaël Ates
for act in acts:
cared = False
91897302 Mikaël Ates
# If there is overlapping between hc period the more recent must
# be used ! That should not happen with hct but might between
# hcd and hct.
if len(hcts) > 0 and hcts[0] and hcts[0].start_date <= act.date and hcts[0].end_date >= act.date:
if count_hct_2 < hcts[0].get_act_number() - hcts[0].get_nb_acts_cared():
acts_treatment.setdefault(patient, []).append((act, hcts[0]))
count_hct_2 = count_hct_2 + 1
6fd159cb Mikaël Ates
cared = True
b2398844 Benjamin Dauvergne
if not cared and len(hcts) > 1 and hcts[1] and hcts[1].start_date <= act.date and hcts[1].end_date >= act.date:
9d3ab5f7 Mikaël Ates
# Ce qui seraient prise en charge
9a64c930 Mikaël Ates
# ne doit pas dépasser la limite de prise en charge du hc
if count_hct_1 < hcts[1].get_act_number() - hcts[1].get_nb_acts_cared():
24eaef1a Mikaël Ates
acts_treatment.setdefault(patient, []).append((act, hcts[1]))
9a64c930 Mikaël Ates
count_hct_1 = count_hct_1 + 1
cared = True
b346a052 Mikaël Ates
if not cared and hcd and hcd.start_date <= act.date and \
(not hcd.end_date or hcd.end_date >= act.date):
91897302 Mikaël Ates
# Ce qui seraient prise en charge
nb_acts_cared = len_acts_cared_diag + count_hcd
# Ne doit pas dépasser la limite de prise en charge du hc
if nb_acts_cared < hcd.get_act_number() :
acts_diagnostic.setdefault(patient, []).append((act, hcd))
count_hcd = count_hcd + 1
9a64c930 Mikaël Ates
cared = True
9d3ab5f7 Mikaël Ates
if not cared:
24eaef1a Mikaël Ates
acts_losts.setdefault(patient, []).append(act)
9d3ab5f7 Mikaël Ates
return (acts_not_locked, days_not_locked, acts_not_valide,
bc7e290e Mikaël Ates
acts_not_billable, acts_pause, acts_diagnostic,
36f1b357 Mikaël Ates
acts_treatment, acts_losts, acts_losts_missing_policy,
acts_losts_missing_birthdate)
6fd159cb Mikaël Ates
523c5aaf Mikaël Ates
def list_acts_for_billing_CMPP_per_patient(patient, end_day, service, acts=None):
6fd159cb Mikaël Ates
acts_not_locked, days_not_locked, acts_not_valide, \
acts_not_billable, acts_pause, acts_billable = \
list_acts_for_billing_first_round(end_day, service, acts=acts, patient=patient)

acts_per_hc = {}
acts_losts = []
if len(acts_billable.keys()) > 1:
raise "Should not find more than one patient"
elif len(acts_billable.keys()) == 1:
# Date de début de la prise en charge ayant servis au dernier acte facturé
lasts_billed = Act.objects.filter(patient=patient, is_billed = True, healthcare__isnull=False).order_by('-date')
last_hc_date = None
if lasts_billed:
last_hc_date = lasts_billed[0].healthcare.start_date
patient, acts = acts_billable.items()[0]
hcd = None
len_acts_cared_diag = 0
try:
hcd = CmppHealthCareDiagnostic.objects.\
filter(patient=patient).latest('start_date')
if not last_hc_date or last_hc_date <= hcd.start_date:
# actes prise en charge par ce hc
len_acts_cared_diag = len(hcd.act_set.all())
else:
# 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.
hcd = None
except:
pass
'''
We take in account the two last treatment healthcare
'''
hcts = None
len_acts_cared_trait = 0
try:
hcts = CmppHealthCareTreatment.objects.\
filter(patient=patient).order_by('-start_date')
except:
pass
# acts are all billable and chronologically ordered
count_hcd = 0
count_hct_1 = 0
count_hct_2 = 0
if hcd:
acts_per_hc[hcd] = []
if len(hcts) > 0:
acts_per_hc[hcts[0]] = []
if len(hcts) > 1:
acts_per_hc[hcts[1]] = []
for act in acts:
cared = False
91897302 Mikaël Ates
# If there is overlapping between hc period the more recent must
# be used ! That should not happen with hct but might between
# hcd and hct.
if len(hcts) > 0 and hcts[0] and hcts[0].start_date <= act.date and hcts[0].end_date >= act.date:
if count_hct_2 < hcts[0].get_act_number() - hcts[0].get_nb_acts_cared():
acts_per_hc[hcts[0]].append(act)
count_hct_2 = count_hct_2 + 1
6fd159cb Mikaël Ates
cared = True
if not cared and len(hcts) > 1 and hcts[1] and hcts[1].start_date <= act.date and hcts[1].end_date >= act.date:
# Ce qui seraient prise en charge
# ne doit pas dépasser la limite de prise en charge du hc
if count_hct_1 < hcts[1].get_act_number() - hcts[1].get_nb_acts_cared():
acts_per_hc[hcts[1]].append(act)
count_hct_1 = count_hct_1 + 1
cared = True
b346a052 Mikaël Ates
if not cared and hcd and hcd.start_date <= act.date and \
(not hcd.end_date or hcd.end_date >= act.date):
91897302 Mikaël Ates
# Ce qui seraient prise en charge
nb_acts_cared = len_acts_cared_diag + count_hcd
# Ne doit pas dépasser la limite de prise en charge du hc
if nb_acts_cared < hcd.get_act_number() :
acts_per_hc[hcd].append(act)
count_hcd = count_hcd + 1
6fd159cb Mikaël Ates
cared = True
if not cared:
acts_losts.append(act)
73de0d6e Mikaël Ates
if len(acts_pause.keys()) == 1:
acts_pause = acts_pause.values()[0]
else:
acts_pause = []
6fd159cb Mikaël Ates
return (acts_not_locked, days_not_locked, acts_not_valide,
acts_not_billable, acts_pause, acts_per_hc, acts_losts)