This repository has been archived on 2023-02-21. You can view files and clone it, but cannot push or open issues or pull requests.
calebasse/calebasse/facturation/models.py

1023 lines
47 KiB
Python

# -*- coding: utf-8 -*-
import tempfile
from datetime import date, datetime
from dateutil.relativedelta import relativedelta
from decimal import Decimal
from django.db import models
from django.db.models import Max, Q
from model_utils import Choices
from calebasse.dossiers.models import PatientRecord
from calebasse.ressources.models import (ServiceLinkedManager, PricePerAct,
HealthCenter)
import list_acts
import progor
from batches import build_batches
def social_security_id_full(nir):
old = nir
try:
# Corse dpt 2A et 2B
minus = 0
if nir[6] in ('A', 'a'):
nir = [c for c in nir]
nir[6] = '0'
nir = ''.join(nir)
minus = 1000000
elif nir[6] in ('B', 'b'):
nir = [c for c in nir]
nir[6] = '0'
nir = ''.join(nir)
minus = 2000000
nir = int(nir) - minus
return old + ' %0.2d' % (97 - (nir % 97))
except Exception, e:
return old + ' 00'
def quarter_start_and_end_dates(today=None):
'''Returns the first and last day of the current quarter'''
if today is None:
today = date.today()
quarter = (today.month - 1) / 3
start_date = date(day=1, month=((quarter*3) + 1), year=today.year)
end_date = start_date + relativedelta(months=3) + relativedelta(days=-1)
return start_date, end_date
class InvoicingManager(ServiceLinkedManager):
def current_for_service(self, service):
'''Return the currently open invoicing'''
if service.name != 'CMPP':
start_date, end_date = quarter_start_and_end_dates()
invoicing, created = \
self.get_or_create(start_date=start_date,
end_date=end_date, service=service)
if created:
invoicing.status = Invoicing.STATUS.closed
invoicing.save()
else:
try:
invoicing = self.get(service=service,
status=Invoicing.STATUS.open)
except Invoicing.DoesNotExist:
today = date.today()
start_date = date(day=today.day, month=today.month, year=today.year)
invoicing, created = self.get_or_create(service=service,
start_date=start_date,
status=Invoicing.STATUS.open)
return invoicing
def last_for_service(self, service):
current = self.current_for_service(service)
last_seq_id = current.seq_id - 1
try:
return self.get(service=service,
seq_id=last_seq_id)
except:
return None
def build_invoices_from_acts(acts_diagnostic, acts_treatment):
invoices = {}
len_invoices = 0
len_invoices_hors_pause = 0
len_acts_invoiced = 0
len_acts_invoiced_hors_pause = 0
pricing = PricePerAct.pricing()
for patient, acts in acts_diagnostic.items():
invoices[patient] = []
act, hc = acts[0]
invoice = dict()
invoice['ppa'] = pricing.price_at_date(act.date)
invoice['year'] = act.date.year
invoice['acts'] = [(act, hc)]
if len(acts) > 1:
for act, hc in acts[1:]:
if invoice['ppa'] != pricing.price_at_date(act.date) or \
invoice['year'] != act.date.year:
invoices[patient].append(invoice)
len_invoices += 1
len_acts_invoiced += len(invoice['acts'])
if not patient.pause:
len_invoices_hors_pause += 1
len_acts_invoiced_hors_pause += len(invoice['acts'])
invoice = dict()
invoice['ppa'] = pricing.price_at_date(act.date)
invoice['year'] = act.date.year
invoice['acts'] = [(act, hc)]
else:
invoice['acts'].append((act, hc))
invoices[patient].append(invoice)
len_invoices += 1
len_acts_invoiced += len(invoice['acts'])
if not patient.pause:
len_invoices_hors_pause += 1
len_acts_invoiced_hors_pause += len(invoice['acts'])
pricing = PricePerAct.pricing()
for patient, acts in acts_treatment.items():
if not patient in invoices:
invoices[patient] = []
act, hc = acts[0]
invoice = dict()
invoice['ppa'] = pricing.price_at_date(act.date)
invoice['year'] = act.date.year
invoice['acts'] = [(act, hc)]
if len(acts) > 1:
for act, hc in acts[1:]:
if invoice['ppa'] != pricing.price_at_date(act.date) or \
invoice['year'] != act.date.year:
invoices[patient].append(invoice)
len_invoices += 1
len_acts_invoiced += len(invoice['acts'])
if not patient.pause:
len_invoices_hors_pause += 1
len_acts_invoiced_hors_pause += len(invoice['acts'])
invoice = dict()
invoice['ppa'] = pricing.price_at_date(act.date)
invoice['year'] = act.date.year
invoice['acts'] = [(act, hc)]
else:
invoice['acts'].append((act, hc))
invoices[patient].append(invoice)
len_invoices += 1
len_acts_invoiced += len(invoice['acts'])
if not patient.pause:
len_invoices_hors_pause += 1
len_acts_invoiced_hors_pause += len(invoice['acts'])
return (invoices, len_invoices, len_invoices_hors_pause,
len_acts_invoiced, len_acts_invoiced_hors_pause)
# The firts cmpp invoicing with calebasse
INVOICING_OFFSET = 134
class Invoicing(models.Model):
'''Represent a batch of invoices:
end_date - only acts before this date will be considered
status - current status of the invoicing
acts - acts bounded to this invoicing when the invoicing is validated
STATUS - the possible status:
- open, the invoicing is open for new acts
- closed, invoicing has been closed, no new acts will be accepted after
the end_date,
- validated,
'''
STATUS = Choices('open', 'closed', 'validated', 'sent')
seq_id = models.IntegerField(blank=True, null=True)
service = models.ForeignKey('ressources.Service', on_delete=models.PROTECT)
start_date = models.DateField(
verbose_name=u'Ouverture de la facturation')
end_date = models.DateField(
verbose_name=u'Clôturation de la facturation',
blank=True,
null=True)
status = models.CharField(
verbose_name=u'Statut',
choices=STATUS,
default=STATUS.open,
max_length=20)
acts = models.ManyToManyField('actes.Act')
objects = InvoicingManager()
class Meta:
unique_together = (('seq_id', 'service'),)
def allocate_seq_id(self):
'''Allocate a new sequence id for a new invoicing.'''
seq_id = 1
max_seq_id = Invoicing.objects.for_service(self.service) \
.aggregate(Max('seq_id'))['seq_id__max']
if not max_seq_id:
if self.service.name == 'CMPP':
seq_id = INVOICING_OFFSET
else:
seq_id = max_seq_id + 1
return seq_id
def list_for_billing(self):
'''Return the acts candidates for billing'''
if self.service.name == 'CMPP':
end_date = self.end_date
if not end_date:
today = date.today()
end_date = date(day=today.day, month=today.month, year=today.year)
return list_acts.list_acts_for_billing_CMPP(end_date,
service=self.service)
elif self.service.name == 'CAMSP':
return list_acts.list_acts_for_billing_CAMSP(self.start_date,
self.end_date, service=self.service)
elif 'SESSAD' in self.service.name:
return list_acts.list_acts_for_billing_SESSAD(self.start_date,
self.end_date, service=self.service)
else:
raise RuntimeError('Unknown service', self.service)
def get_batches(self):
batches = list()
for hc, bs in build_batches(self).iteritems():
for batch in bs:
amount_rejected = sum([invoice.decimal_amount
for invoice in batch.invoices
if invoice.rejected])
versement = batch.total - amount_rejected
batches.append((hc, batch, amount_rejected, versement))
return batches
def get_stats_per_price_per_year(self):
stats_final = dict()
stats_final['total'] = (0, 0)
stats_final['detail'] = dict()
if self.service.name != 'CMPP' or \
self.status in (Invoicing.STATUS.open,
Invoicing.STATUS.closed):
return stats_final
stats = stats_final['detail']
invoices = self.invoice_set.all()
for invoice in invoices:
if not invoice.list_dates:
continue
# All acts of an invoice are the same year and at the same price
dates = invoice.list_dates.split('$')
year = datetime.strptime(dates[0], "%d/%m/%Y").year
ppa = invoice.decimal_ppa
if year not in stats:
stats[year] = dict()
if ppa not in stats[year]:
stats[year][ppa] = (0, 0)
nb_acts, amount = stats[year][ppa]
nb_acts += len(dates)
amount += invoice.decimal_amount
stats[year][ppa] = (nb_acts, amount)
nb_acts_f, amount_f = stats_final['total']
nb_acts_f += invoice.acts.count()
amount_f += invoice.decimal_amount
stats_final['total'] = (nb_acts_f, amount_f)
return stats_final
def get_stats_or_validate(self, commit=False):
'''
If the invoicing is in state open or closed and commit is False
Return the stats of the billing
If the invoicing is in state open or closed and commit is True
Proceed to invoices creation, healthcare charging, acts as billed
Return the stats of the billing
If the invoicing is in state validated or sent
Return the stats of the billing
'''
days_not_locked = 0
if self.service.name == 'CMPP':
if self.status in (Invoicing.STATUS.open,
Invoicing.STATUS.closed):
'''
The stats are build dynamiccaly according to the
acts billable and the healthcares
'''
(acts_not_locked, days_not_locked, acts_not_valide,
acts_not_billable, acts_pause, acts_diagnostic,
acts_treatment, acts_losts,
acts_losts_missing_policy,
acts_losts_missing_birthdate) = \
self.list_for_billing()
(invoices, len_invoices, len_invoices_hors_pause,
len_acts_invoiced, len_acts_invoiced_hors_pause) = \
build_invoices_from_acts(acts_diagnostic, acts_treatment)
len_patient_invoiced = len(invoices.keys())
len_patient_invoiced_hors_pause = 0
for patient in invoices.keys():
if not patient.pause:
len_patient_invoiced_hors_pause = len_patient_invoiced_hors_pause + 1
patients = set(acts_diagnostic.keys() + acts_treatment.keys() + \
acts_losts.keys() + acts_pause.keys() + acts_losts_missing_policy.keys() + \
acts_losts_missing_birthdate.keys())
patients_stats = []
len_patient_with_lost_acts = 0
len_acts_lost = 0
len_patient_acts_paused = 0
len_acts_paused = 0
len_patient_with_lost_acts_missing_policy = 0
len_acts_losts_missing_policy = 0
len_patient_with_lost_acts_missing_birthdate = 0
len_acts_losts_missing_birthdate = 0
batches = {}
last_batch = {}
for patient in patients:
dic = {}
if patient in invoices.keys():
dic['invoices'] = invoices[patient]
if commit and not patient.pause:
policy_holder = patient.policyholder
try:
address = unicode(policy_holder.addresses.filter(place_of_life=True)[0])
except:
try:
address = unicode(policy_holder.addresses.all()[0])
except:
address = u''
invoice_kwargs = dict(
patient_id=patient.id,
patient_last_name=patient.last_name,
patient_first_name=patient.first_name,
patient_social_security_id=patient.social_security_id or '',
patient_birthdate=patient.birthdate,
patient_twinning_rank=patient.twinning_rank,
patient_healthcenter=patient.health_center,
patient_other_health_center=patient.other_health_center or '',
patient_entry_date=patient.entry_date,
patient_exit_date=patient.exit_date,
policy_holder_id=policy_holder.id,
policy_holder_last_name=policy_holder.last_name,
policy_holder_first_name=policy_holder.first_name,
policy_holder_social_security_id=policy_holder.social_security_id,
policy_holder_other_health_center=policy_holder.other_health_center or '',
policy_holder_healthcenter=policy_holder.health_center,
policy_holder_address=address)
for invoice in invoices[patient]:
ppa = invoice['ppa'] * 100
acts = invoice['acts']
amount = ppa * len(acts)
list_dates = '$'.join([act.date.strftime('%d/%m/%Y') for act, hc in acts])
in_o = Invoice(
invoicing=self,
ppa=ppa,
amount=amount,
list_dates = list_dates,
**invoice_kwargs)
in_o.save()
for act, hc in acts:
act.is_billed = True
act.healthcare = hc
act.save()
in_o.acts.add(act)
in_o.first_tag = acts[0][0].get_hc_tag()
in_o.save()
# calcule le numero de lot pour cette facture
hc = in_o.health_center
hc_dest = hc.hc_invoice or hc
dest = hc_dest.large_regime.code + ' ' + hc_dest.dest_organism
if dest not in batches:
batches[dest] = {}
if hc not in batches[dest]:
nb1 = Invoice.objects.new_batch_number(health_center=hc_dest, invoicing=self)
nb2 = Invoice.objects.new_batch_number(health_center=hc, invoicing=self)
nb = max(nb1, nb2, last_batch.get(dest,0) + 1)
last_batch[dest] = nb
batches[dest][hc] = [{ 'batch': nb, 'size': 2, 'invoices': [], }]
# pas plus de 950 lignes par lot (le fichier B2 final ne doit
# pas depasser 999 lignes au total) :
b = batches[dest][hc].pop()
b2_size = in_o.acts.count() + 2
if (b['size'] + b2_size) > 950:
batches[dest][hc].append(b)
nb = last_batch.get(dest, 0) + 1
last_batch[dest] = nb
b = {'batch': nb, 'size': 2, 'invoices': []}
b['invoices'].append(in_o)
b['size'] += b2_size
batches[dest][hc].append(b)
in_o.batch = b['batch']
in_o.save()
if patient in acts_losts.keys():
# TODO: More details about healthcare
dic['losts'] = acts_losts[patient]
len_patient_with_lost_acts += 1
len_acts_lost += len(acts_losts[patient])
if patient in acts_pause.keys():
dic['acts_paused'] = acts_pause[patient]
len_patient_acts_paused += 1
len_acts_paused += len(acts_pause[patient])
if patient in acts_losts_missing_policy.keys():
# TODO: More details about healthcare
dic['losts_missing_policy'] = acts_losts_missing_policy[patient]
len_patient_with_lost_acts_missing_policy += 1
len_acts_losts_missing_policy += len(acts_losts_missing_policy[patient])
if patient in acts_losts_missing_birthdate.keys():
dic['losts_missing_birthdate'] = acts_losts_missing_birthdate[patient]
len_patient_with_lost_acts_missing_birthdate += 1
len_acts_losts_missing_birthdate += len(acts_losts_missing_birthdate[patient])
if dic:
patients_stats.append((patient, dic))
patients_stats = sorted(patients_stats, key=lambda patient: (patient[0].last_name, patient[0].first_name))
len_patients = len(patients_stats)
if commit:
self.status = Invoicing.STATUS.validated
self.save()
else:
'''
Grab stats from the invoices
'''
len_patients = 0
len_invoices = 0
len_acts_invoiced = 0
patients_stats = {}
days_not_locked = []
invoices = self.invoice_set.all()
patients = {}
for invoice in invoices:
len_invoices = len_invoices + 1
if invoice.list_dates:
len_acts_invoiced += len(invoice.list_dates.split('$'))
patient = None
if not invoice.patient_id in patients.keys():
try:
patient = PatientRecord.objects.get(id=invoice.patient_id)
except:
patient = PatientRecord(last_name=invoice.patient_last_name, first_name=invoice.patient_first_name)
patients[invoice.patient_id] = patient
len_patients = len_patients + 1
patients_stats[patient] = {}
patients_stats[patient]['invoices'] = []
else:
patient = patients[invoice.patient_id]
patients_stats[patient]['invoices'].append(invoice)
patients_stats = sorted(patients_stats.items(), key=lambda patient: (patient[0].last_name, patient[0].first_name))
# all patients in the invoicing are invoiced
len_patient_invoiced = 0
# These stats are not necessary because excluded form the validated invoicing
len_invoices_hors_pause = 0
len_acts_invoiced_hors_pause = 0
len_patient_invoiced_hors_pause = 0
len_acts_lost = 0
len_patient_with_lost_acts = 0
len_patient_acts_paused = 0
len_acts_paused = 0
len_patient_with_lost_acts_missing_policy = 0
len_acts_losts_missing_policy = 0
len_patient_with_lost_acts_missing_birthdate = 0
len_acts_losts_missing_birthdate = 0
return (len_patients, len_invoices, len_invoices_hors_pause,
len_acts_invoiced, len_acts_invoiced_hors_pause,
len_patient_invoiced, len_patient_invoiced_hors_pause,
len_acts_lost, len_patient_with_lost_acts, patients_stats,
days_not_locked, len_patient_acts_paused,
len_acts_paused, len_acts_losts_missing_policy,
len_patient_with_lost_acts_missing_policy,
len_acts_losts_missing_birthdate,
len_patient_with_lost_acts_missing_birthdate)
elif self.service.name == 'CAMSP':
if self.status in Invoicing.STATUS.closed:
(acts_not_locked, days_not_locked, acts_not_valide,
acts_not_billable, acts_pause, acts_bad_state,
acts_accepted, patients_missing_policy) = self.list_for_billing()
len_patient_pause = 0
len_patient_hors_pause = 0
len_acts_pause = 0
len_acts_hors_pause = 0
len_patient_acts_paused = 0
len_acts_paused = 0
patients = set(acts_accepted.keys() + acts_pause.keys())
patients_stats = []
for patient in patients:
dic = {}
if patient in acts_accepted.keys():
acts = acts_accepted[patient]
dic['accepted'] = acts
if patient.pause:
len_patient_pause += 1
len_acts_pause += len(acts)
else:
len_patient_hors_pause += 1
len_acts_hors_pause += len(acts)
if commit:
policy_holder = patient.policyholder
try:
address = unicode(policy_holder.addresses.filter(place_of_life=True)[0])
except:
try:
address = unicode(policy_holder.addresses.all()[0])
except:
address = u''
invoice_kwargs = dict(
patient_id=patient.id,
patient_last_name=patient.last_name,
patient_first_name=patient.first_name,
patient_social_security_id=patient.social_security_id or '',
patient_birthdate=patient.birthdate,
patient_twinning_rank=patient.twinning_rank,
patient_healthcenter=patient.health_center,
patient_other_health_center=patient.other_health_center or '',
patient_entry_date=patient.entry_date,
patient_exit_date=patient.exit_date,
policy_holder_id=policy_holder.id,
policy_holder_last_name=policy_holder.last_name,
policy_holder_first_name=policy_holder.first_name,
policy_holder_social_security_id=policy_holder.social_security_id or '',
policy_holder_other_health_center=policy_holder.other_health_center or '',
policy_holder_healthcenter=policy_holder.health_center,
policy_holder_address=address)
if policy_holder.management_code:
management_code = policy_holder.management_code
invoice_kwargs['policy_holder_management_code'] = management_code.code
invoice_kwargs['policy_holder_management_code_name'] = management_code.name
list_dates = '$'.join([act.date.strftime('%d/%m/%Y') for act in acts])
invoice = Invoice(
invoicing=self,
ppa=0, amount=0,
list_dates=list_dates,
**invoice_kwargs)
invoice.save()
for act in acts:
act.is_billed = True
act.save()
invoice.acts.add(act)
if patient in acts_pause.keys():
dic['acts_paused'] = acts_pause[patient]
len_patient_acts_paused += 1
len_acts_paused += len(acts_pause[patient])
patients_stats.append((patient, dic))
patients_stats = sorted(patients_stats, key=lambda patient: (patient[0].last_name, patient[0].first_name))
if commit:
self.status = Invoicing.STATUS.validated
self.save()
else:
patients_stats = {}
len_patient_pause = 0
len_patient_hors_pause = 0
len_acts_pause = 0
len_acts_hors_pause = 0
len_patient_acts_paused = 0
len_acts_paused = 0
days_not_locked = []
patients_missing_policy = []
invoices = self.invoice_set.all()
for invoice in invoices:
len_patient_hors_pause += 1
if invoice.list_dates:
len_acts_hors_pause += len(invoice.list_dates.split('$'))
patient = None
try:
patient = PatientRecord.objects.get(id=invoice.patient_id)
except:
patient = PatientRecord(last_name=invoice.patient_last_name, first_name=invoice.patient_first_name)
patients_stats[patient] = {}
patients_stats[patient]['accepted'] = invoice.acts.all()
patients_stats = sorted(patients_stats.items(), key=lambda patient: (patient[0].last_name, patient[0].first_name))
return (len_patient_pause, len_patient_hors_pause,
len_acts_pause, len_acts_hors_pause, patients_stats,
days_not_locked, len_patient_acts_paused,
len_acts_paused, patients_missing_policy)
else:
if self.status in Invoicing.STATUS.closed:
(acts_not_locked, days_not_locked, acts_not_valide,
acts_not_billable, acts_pause, acts_bad_state,
acts_missing_valid_notification, acts_accepted,
patients_missing_policy) = \
self.list_for_billing()
len_patient_pause = 0
len_patient_hors_pause = 0
len_acts_pause = 0
len_acts_hors_pause = 0
len_patient_acts_paused = 0
len_acts_paused = 0
len_patient_missing_notif = 0
len_acts_missing_notif = 0
patients = set(acts_accepted.keys() + \
acts_missing_valid_notification.keys() + \
acts_pause.keys())
patients_stats = []
patients_missing_notif = []
for patient in patients:
dic = {}
acts_to_commit = []
if patient in acts_accepted.keys():
acts = acts_accepted[patient]
acts_to_commit.extend(acts)
dic['accepted'] = acts
if patient.pause:
len_patient_pause += 1
len_acts_pause += len(acts)
else:
len_patient_hors_pause += 1
len_acts_hors_pause += len(acts)
if patient in acts_missing_valid_notification.keys():
patients_missing_notif.append(patient)
acts = acts_missing_valid_notification[patient]
acts_to_commit.extend(acts)
dic['missings'] = acts
len_patient_missing_notif += 1
len_acts_missing_notif += len(acts)
if patient.pause:
if not 'accepted' in dic:
len_patient_pause += 1
len_acts_pause += len(acts)
else:
if not 'accepted' in dic:
len_patient_hors_pause += 1
len_acts_hors_pause += len(acts)
if commit and acts_to_commit:
policy_holder = patient.policyholder
try:
address = unicode(policy_holder.addresses.filter(place_of_life=True)[0])
except:
try:
address = unicode(policy_holder.addresses.all()[0])
except:
address = u''
invoice_kwargs = dict(
patient_id=patient.id,
patient_last_name=patient.last_name,
patient_first_name=patient.first_name,
patient_social_security_id=patient.social_security_id or '',
patient_birthdate=patient.birthdate,
patient_twinning_rank=patient.twinning_rank,
patient_healthcenter=patient.health_center,
patient_other_health_center=patient.other_health_center or '',
patient_entry_date=patient.entry_date,
patient_exit_date=patient.exit_date,
policy_holder_id=policy_holder.id,
policy_holder_last_name=policy_holder.last_name,
policy_holder_first_name=policy_holder.first_name,
policy_holder_social_security_id=policy_holder.social_security_id or '',
policy_holder_other_health_center=policy_holder.other_health_center or '',
policy_holder_healthcenter=policy_holder.health_center,
policy_holder_address=address)
if policy_holder.management_code:
management_code = policy_holder.management_code
invoice_kwargs['policy_holder_management_code'] = management_code.code
invoice_kwargs['policy_holder_management_code_name'] = management_code.name
list_dates = '$'.join([act.date.strftime('%d/%m/%Y') for act in acts_to_commit])
invoice = Invoice(
invoicing=self,
ppa=0, amount=0,
list_dates=list_dates,
**invoice_kwargs)
invoice.save()
for act in acts_to_commit:
act.is_billed = True
act.save()
invoice.acts.add(act)
if patient in acts_pause.keys():
dic['acts_paused'] = acts_pause[patient]
len_patient_acts_paused += 1
len_acts_paused += len(acts_pause[patient])
patients_stats.append((patient, dic))
patients_stats = sorted(patients_stats, key=lambda patient: (patient[0].last_name, patient[0].first_name))
if commit:
self.status = Invoicing.STATUS.validated
self.save()
else:
patients_stats = {}
len_patient_pause = 0
len_patient_hors_pause = 0
len_acts_pause = 0
len_acts_hors_pause = 0
len_patient_acts_paused = 0
len_acts_paused = 0
len_patient_missing_notif = 0
len_acts_missing_notif = 0
days_not_locked = []
patients_missing_policy = []
patients_missing_notif = []
invoices = self.invoice_set.all()
for invoice in invoices:
len_patient_hors_pause += 1
if invoice.list_dates:
len_acts_hors_pause += len(invoice.list_dates.split('$'))
patient = None
try:
patient = PatientRecord.objects.get(id=invoice.patient_id)
except:
patient = PatientRecord(last_name=invoice.patient_last_name, first_name=invoice.patient_first_name)
patients_stats[patient] = {}
patients_stats[patient]['accepted'] = invoice.acts.all()
patients_stats = sorted(patients_stats.items(), key=lambda patient: (patient[0].last_name, patient[0].first_name))
return (len_patient_pause, len_patient_hors_pause,
len_acts_pause, len_acts_hors_pause,
len_patient_missing_notif, len_acts_missing_notif,
patients_stats, days_not_locked,
len_patient_acts_paused, len_acts_paused,
patients_missing_policy, patients_missing_notif)
def save(self, *args, **kwargs):
if not self.seq_id:
self.seq_id = self.allocate_seq_id()
super(Invoicing, self).save(*args, **kwargs)
def close(self, end_date=None):
'''Close an open invoicing'''
if self.service.name != 'CMPP':
raise RuntimeError('closing Invoicing is only for the CMPP')
if self.status != Invoicing.STATUS.open:
raise RuntimeError('closing an un-opened Invoicing')
if not end_date:
today = date.today()
end_date = date(day=today.day, month=today.month, year=today.year)
if end_date < self.start_date:
end_date = self.start_date + relativedelta(days=1)
self.status = Invoicing.STATUS.closed
self.end_date = end_date
self.save()
start_date = self.end_date + relativedelta(days=1)
invoicing = Invoicing(service=self.service,
start_date=start_date,
status=Invoicing.STATUS.open)
invoicing.save()
return invoicing
def export_for_accounting(self):
'''
Il faut separer les ecritures pour l'année en cours et les années
passées.
'''
def add_ecriture(health_center, amount):
accounting_number = '0' + health_center.accounting_number[1:]
'''Le nom du compte ne devrait pas etre important et seul compter
le numéro de compte'''
name = health_center.name
if len(name) > 30:
name = name[0:30]
ecriture = progor.EcritureComptable(date, accounting_number,
name, cycle, debit=amount)
imputation = progor.ImputationAnalytique(debit=amount)
ecriture.add_imputation(imputation)
journal.add_ecriture(ecriture)
journal = progor.IdentificationJournal()
ecritures_current = dict()
ecritures_past = dict()
total_current = 0
total_past = 0
date = self.end_date.strftime("%d/%m/%Y")
cycle = str(self.seq_id)
invoice_year = datetime(self.end_date.year, 1, 1).date()
for invoice in self.invoice_set.all():
hc = invoice.health_center.hc_invoice or invoice.health_center
if invoice.end_date < invoice_year:
ecritures_past.setdefault(hc, 0)
ecritures_past[hc] += invoice.amount
total_past += invoice.amount
else:
ecritures_current.setdefault(hc, 0)
ecritures_current[hc] += invoice.amount
total_current += invoice.amount
'''Ce qui est facturé aux caisses est en débit'''
for health_center, amount in ecritures_past.items():
add_ecriture(health_center, amount)
for health_center, amount in ecritures_current.items():
add_ecriture(health_center, amount)
'''On équilibre avec du produit en crédit'''
#Produit années précédentes
if total_past:
ecriture = progor.EcritureComptable(date, '77200000',
'PRODUITS EXERCICES ANTERIEURS', cycle, credit=total_past, type_compte='0')
imputation = progor.ImputationAnalytique(credit=total_past)
ecriture.add_imputation(imputation)
journal.add_ecriture(ecriture)
#Produit année en cours
ecriture = progor.EcritureComptable(date, '73130000',
'PRODUITS EXERCICE', cycle, credit=total_current, type_compte='0')
imputation = progor.ImputationAnalytique(credit=total_current)
ecriture.add_imputation(imputation)
journal.add_ecriture(ecriture)
content = journal.render()
service = self.service
now = datetime.now()
output_file = tempfile.NamedTemporaryFile(prefix='%s-export-%s-' %
(service.slug, self.id), suffix='-%s.txt' % now, delete=False)
output_file.write(content)
return output_file.name
#class PricePerAct(models.Model):
# price = models.IntegerField()
# start_date = models.DateField(
# verbose_name=u"Prise d'effet",
# default=date(day=5,month=1,year=1970))
# end_date = models.DateField(
# verbose_name=u"Fin d'effet",
# blank=True,
# null=True)
# @classmethod
# def get_price(cls, at_date=None):
# if not at_date:
# at_date = date.today()
# if isinstance(at_date, datetime):
# at_date = date(day=at_date.day, month=at_date.month,
# year=at_date.year)
# found = cls.objects.filter(start_date__lte = at_date).latest('start_date')
# if not found:
# raise Exception('No price to apply')
# return found.price
# def __unicode__(self):
# val = str(self.price) + ' from ' + str(self.start_date)
# if self.end_date:
# val = val + ' to ' + str(self.end_date)
# return val
#def add_price(price, start_date=None):
# price_o = None
# ppas = PricePerAct.objects.all()
# if ppas:
# if not start_date:
# raise Exception('A start date is mandatory to add a new Price')
# last_ppa = PricePerAct.objects.latest('start_date')
# if last_ppa.start_date >= start_date:
# raise Exception('The new price cannot apply before the price that currently applies.')
# if last_ppa.end_date and last_ppa.end_date != (start_date + relativedelta(days=-1)):
# raise Exception('The new price should apply the day after the last price ends.')
# last_ppa.end_date = start_date + relativedelta(days=-1)
# last_ppa.save()
# if not start_date:
# price_o = PricePerAct(price=price)
# else:
# price_o = PricePerAct(price=price, start_date=start_date)
# price_o.save()
# return price_o
PREVIOUS_MAX_BATCH_NUMBERS = {
'CF00000004001110': 34,
'CT00000001016421': 1,
'CT00000001016422': 141,
'CT00000001025691': 20,
'CT00000002011011': 8,
'CT00000002381421': 15,
'MA00000002381421': 48,
'SM00000091007381': 98, # faure 43
'SM00000091007421': 98, # faure 44
'SM00000091007422': 98, # faure 45
'SM00000091007431': 98, # faure 46
'SM00000091007691': 98, # faure 47
'SM00000091007764': 98, # faure 48
'SM00000092001422': 14,
'SM00000092001691': 13,
'SM00000099038939': 24,
}
class InvoiceManager(models.Manager):
def new_batch_number(self, health_center, invoicing):
'''Compute the next batch number for the given health center'''
global PREVIOUS_MAX_BATCH_NUMBERS
hcs = HealthCenter.objects.filter(hc_invoice=health_center)
agg = self \
.filter(invoicing__service=invoicing.service) \
.filter(invoicing__seq_id__lt=invoicing.seq_id) \
.filter(
Q(patient_healthcenter=health_center,
policy_holder_healthcenter__isnull=True)|
Q(policy_holder_healthcenter=health_center) |
Q(patient_healthcenter__in=hcs,
policy_holder_healthcenter__isnull=True)|
Q(policy_holder_healthcenter__in=hcs)) \
.aggregate(Max('batch'))
max_bn = agg['batch__max']
if max_bn is None:
max_bn = PREVIOUS_MAX_BATCH_NUMBERS.get(health_center.b2_000(), 0)
else:
max_bn = max(max_bn, PREVIOUS_MAX_BATCH_NUMBERS.get(health_center.b2_000(), 0))
return max_bn + 1
class Invoice(models.Model):
objects = InvoiceManager()
number = models.IntegerField(blank=True, null=True)
batch = models.IntegerField(blank=True, null=True)
# the patient can be modified (or even deleted) after invoice creation, so
# we copy his informations here
patient_id = models.IntegerField(blank=True, null=True)
patient_last_name = models.CharField(max_length=128,
verbose_name=u'Nom du patient', default='', blank=True)
patient_first_name = models.CharField(max_length=128,
verbose_name=u'Prénom(s) du patient', default='', blank=True)
patient_social_security_id = models.CharField(max_length=13,
verbose_name=u"NIR", default='', blank=True)
patient_birthdate = models.DateField(verbose_name=u"Date de naissance",
null=True, blank=True)
patient_twinning_rank = models.IntegerField(
verbose_name=u"Rang (gémellité)",
null=True, blank=True)
patient_healthcenter = models.ForeignKey('ressources.HealthCenter',
verbose_name=u"Centre d'assurance maladie",
related_name='related_by_patient_invoices',
null=True, blank=True)
patient_entry_date = models.DateField(verbose_name=u'Date d\'entrée du patient',
blank=True, null=True)
patient_exit_date = models.DateField(verbose_name=u'Date de sortie du patient',
blank=True, null=True)
patient_other_health_center = models.CharField(
verbose_name=u"Centre spécifique", max_length=4, default='',
blank=True)
# policy holder informations
policy_holder_id = models.IntegerField(blank=True, null=True)
policy_holder_last_name = models.CharField(max_length=128,
verbose_name=u'Nom de l\'assuré', default='', blank=True)
policy_holder_first_name = models.CharField(max_length=128,
verbose_name=u'Prénom(s) de l\' assuré', default='', blank=True)
policy_holder_social_security_id = models.CharField(max_length=13,
verbose_name=u"NIR de l\'assuré", default='', blank=True)
policy_holder_healthcenter = models.ForeignKey('ressources.HealthCenter',
verbose_name=u"Centre d'assurance maladie de l\'assuré",
related_name='related_by_policy_holder_invoices',
null=True, blank=True)
policy_holder_other_health_center = models.CharField(
verbose_name=u"Centre spécifique de l\'assuré", max_length=4,
default='', blank=True)
policy_holder_address = models.CharField(max_length=128,
verbose_name=u'Adresse de l\'assuré', default='', blank=True)
policy_holder_management_code = models.CharField(max_length=10,
verbose_name=u'Code de gestion', default='', blank=True)
policy_holder_management_code_name = models.CharField(max_length=256,
verbose_name=u'Libellé du code de gestion', default='', blank=True)
created = models.DateTimeField(u'Création', auto_now_add=True)
invoicing = models.ForeignKey('facturation.Invoicing',
on_delete=models.PROTECT)
acts = models.ManyToManyField('actes.Act')
list_dates = models.CharField(max_length=8192, blank=True, null=True)
first_tag = models.CharField(max_length=128, blank=True, null=True)
amount = models.IntegerField()
ppa = models.IntegerField()
rejected = models.BooleanField(verbose_name=u"Rejeté", default=False)
def save(self, *args, **kwargs):
if not self.number:
invoicing = self.invoicing
self.number = invoicing.seq_id * 1000000 + 1
max_number = invoicing.invoice_set.aggregate(Max('number'))['number__max']
if max_number:
self.number = max_number + 1
super(Invoice, self).save(*args, **kwargs)
@property
def start_date(self):
res = date.max
for d in self.list_dates.split('$'):
date_o = datetime.strptime(d, '%d/%m/%Y').date()
res = min(res, date_o)
return res
@property
def health_center(self):
return self.policy_holder_healthcenter or self.patient_healthcenter
@property
def end_date(self):
res = date.min
for d in self.list_dates.split('$'):
date_o = datetime.strptime(d, '%d/%m/%Y').date()
res = max(res, date_o)
return res
@property
def decimal_amount(self):
return Decimal(self.amount) / Decimal(100)
@property
def decimal_amount_corrected(self):
amount = 0
for d, ppa in self.list_dates:
amount += ppa
return Decimal(amount) / Decimal(100)
@property
def decimal_ppa(self):
return Decimal(self.ppa) / Decimal(100)
@property
def kind(self):
if not self.first_tag:
return None
return self.first_tag[0]
@property
def patient_social_security_id_full(self):
if self.patient_social_security_id:
return social_security_id_full(self.patient_social_security_id)
else:
return ''
@property
def policy_holder_social_security_id_full(self):
if self.policy_holder_social_security_id:
return social_security_id_full(self.policy_holder_social_security_id)
else:
return ''
def __unicode__(self):
return "Facture %d de %d euros (%d actes)" % (self.number, self.amount, len(self.acts.all()))