Project

General

Profile

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

calebasse / calebasse / facturation / models.py @ 3d7a840a

1
# -*- coding: utf-8 -*-
2
import tempfile
3

    
4
from datetime import date, datetime
5
from dateutil.relativedelta import relativedelta
6
from decimal import Decimal
7

    
8
from django.db import models
9
from django.db.models import Max, Q
10

    
11
from model_utils import Choices
12

    
13
from calebasse.dossiers.models import PatientRecord
14
from calebasse.ressources.models import ServiceLinkedManager, PricePerAct
15

    
16
import list_acts
17
import progor
18

    
19
def social_security_id_full(nir):
20
    old = nir
21
    try:
22
        # Corse dpt 2A et 2B
23
        minus = 0
24
        if nir[6] in ('A', 'a'):
25
            nir = [c for c in nir]
26
            nir[6] = '0'
27
            nir = ''.join(nir)
28
            minus = 1000000
29
        elif nir[6] in ('B', 'b'):
30
            nir = [c for c in nir]
31
            nir[6] = '0'
32
            nir = ''.join(nir)
33
            minus = 2000000
34
        nir = int(nir) - minus
35
        return old + ' %0.2d' % (97 - (nir % 97))
36
    except Exception, e:
37
        return old + ' 00'
38

    
39
def quarter_start_and_end_dates(today=None):
40
    '''Returns the first and last day of the current quarter'''
41
    if today is None:
42
        today = date.today()
43
    quarter = (today.month - 1) / 3
44
    start_date = date(day=1, month=((quarter*3) + 1), year=today.year)
45
    end_date = start_date + relativedelta(months=3) + relativedelta(days=-1)
46
    return start_date, end_date
47

    
48
class InvoicingManager(ServiceLinkedManager):
49
    def current_for_service(self, service):
50
        '''Return the currently open invoicing'''
51
        if service.name != 'CMPP':
52
            start_date, end_date = quarter_start_and_end_dates()
53
            invoicing, created = \
54
                self.get_or_create(start_date=start_date,
55
                end_date=end_date, service=service)
56
            if created:
57
                invoicing.status = Invoicing.STATUS.closed
58
                invoicing.save()
59
        else:
60
            try:
61
                invoicing = self.get(service=service,
62
                        status=Invoicing.STATUS.open)
63
            except Invoicing.DoesNotExist:
64
                today = date.today()
65
                start_date = date(day=today.day, month=today.month, year=today.year)
66
                invoicing, created = self.get_or_create(service=service,
67
                    start_date=start_date,
68
                    status=Invoicing.STATUS.open)
69
        return invoicing
70

    
71
    def last_for_service(self, service):
72
        current = self.current_for_service(service)
73
        last_seq_id = current.seq_id - 1
74
        try:
75
            return self.get(service=service,
76
                seq_id=last_seq_id)
77
        except:
78
            return None
79

    
80

    
81
def build_invoices_from_acts(acts_diagnostic, acts_treatment):
82
    invoices = {}
83
    len_invoices = 0
84
    len_invoices_hors_pause = 0
85
    len_acts_invoiced = 0
86
    len_acts_invoiced_hors_pause = 0
87
    pricing = PricePerAct.pricing()
88
    for patient, acts in acts_diagnostic.items():
89
        invoices[patient] = []
90
        act, hc = acts[0]
91
        invoice = dict()
92
        invoice['ppa'] = pricing.price_at_date(act.date)
93
        invoice['year'] = act.date.year
94
        invoice['acts'] = [(act, hc)]
95
        if len(acts) > 1:
96
            for act, hc in acts[1:]:
97
                if invoice['ppa'] != pricing.price_at_date(act.date) or \
98
                        invoice['year'] != act.date.year:
99
                    invoices[patient].append(invoice)
100
                    len_invoices = len_invoices + 1
101
                    len_acts_invoiced = len_acts_invoiced + len(invoice['acts'])
102
                    if not patient.pause:
103
                        len_invoices_hors_pause = len_invoices_hors_pause + 1
104
                        len_acts_invoiced_hors_pause = len_acts_invoiced_hors_pause + len(invoice['acts'])
105
                    invoice = dict()
106
                    invoice['ppa'] = pricing.price_at_date(act.date)
107
                    invoice['year'] = act.date.year
108
                    invoice['acts'] = [(act, hc)]
109
                else:
110
                    invoice['acts'].append((act, hc))
111
        invoices[patient].append(invoice)
112
        len_invoices = len_invoices + 1
113
        len_acts_invoiced = len_acts_invoiced + len(invoice['acts'])
114
        if not patient.pause:
115
            len_invoices_hors_pause = len_invoices_hors_pause + 1
116
            len_acts_invoiced_hors_pause = len_acts_invoiced_hors_pause + len(invoice['acts'])
117
    pricing = PricePerAct.pricing()
118
    for patient, acts in acts_treatment.items():
119
        if not patient in invoices:
120
            invoices[patient] = []
121
        act, hc = acts[0]
122
        invoice = dict()
123
        invoice['ppa'] = pricing.price_at_date(act.date)
124
        invoice['year'] = act.date.year
125
        invoice['acts'] = [(act, hc)]
126
        if len(acts) > 1:
127
            for act, hc in acts[1:]:
128
                if invoice['ppa'] != pricing.price_at_date(act.date) or \
129
                        invoice['year'] != act.date.year:
130
                    invoices[patient].append(invoice)
131
                    len_invoices = len_invoices + 1
132
                    len_acts_invoiced = len_acts_invoiced + len(invoice['acts'])
133
                    if not patient.pause:
134
                        len_invoices_hors_pause = len_invoices_hors_pause + 1
135
                        len_acts_invoiced_hors_pause = len_acts_invoiced_hors_pause + len(invoice['acts'])
136
                    invoice = dict()
137
                    invoice['ppa'] = pricing.price_at_date(act.date)
138
                    invoice['year'] = act.date.year
139
                    invoice['acts'] = [(act, hc)]
140
                else:
141
                    invoice['acts'].append((act, hc))
142
        invoices[patient].append(invoice)
143
        len_invoices = len_invoices + 1
144
        len_acts_invoiced = len_acts_invoiced + len(invoice['acts'])
145
        if not patient.pause:
146
            len_invoices_hors_pause = len_invoices_hors_pause + 1
147
            len_acts_invoiced_hors_pause = len_acts_invoiced_hors_pause + len(invoice['acts'])
148
    return (invoices, len_invoices, len_invoices_hors_pause,
149
        len_acts_invoiced, len_acts_invoiced_hors_pause)
150

    
151
# The firts cmpp invoicing with calebasse
152
INVOICING_OFFSET = 134
153

    
154
class Invoicing(models.Model):
155
    '''Represent a batch of invoices:
156

    
157
       end_date - only acts before this date will be considered
158
       status   - current status of the invoicing
159
       acts     - acts bounded to this invoicing when the invoicing is validated
160

    
161
       STATUS - the possible status:
162
        - open, the invoicing is open for new acts
163
        - closed, invoicing has been closed, no new acts will be accepted after
164
          the end_date,
165
        - validated,
166
    '''
167
    STATUS = Choices('open', 'closed', 'validated', 'sent')
168

    
169
    seq_id = models.IntegerField(blank=True, null=True)
170

    
171
    service = models.ForeignKey('ressources.Service', on_delete='PROTECT')
172

    
173
    start_date = models.DateField(
174
            verbose_name=u'Ouverture de la facturation')
175

    
176
    end_date = models.DateField(
177
            verbose_name=u'Clôturation de la facturation',
178
            blank=True,
179
            null=True)
180

    
181
    status = models.CharField(
182
            verbose_name=u'Statut',
183
            choices=STATUS,
184
            default=STATUS.open,
185
            max_length=20)
186

    
187
    acts = models.ManyToManyField('actes.Act')
188

    
189
    objects = InvoicingManager()
190

    
191
    class Meta:
192
        unique_together = (('seq_id', 'service'),)
193

    
194
    def allocate_seq_id(self):
195
        '''Allocate a new sequence id for a new invoicing.'''
196
        seq_id = 1
197
        max_seq_id = Invoicing.objects.for_service(self.service) \
198
                .aggregate(Max('seq_id'))['seq_id__max']
199
        if not max_seq_id:
200
            if self.service.name == 'CMPP':
201
                seq_id = INVOICING_OFFSET
202
        else:
203
            seq_id = max_seq_id + 1
204
        return seq_id
205

    
206
    def list_for_billing(self):
207
        '''Return the acts candidates for billing'''
208
        if self.service.name == 'CMPP':
209
            end_date = self.end_date
210
            if not end_date:
211
                today = date.today()
212
                end_date = date(day=today.day, month=today.month, year=today.year)
213
            return list_acts.list_acts_for_billing_CMPP_2(end_date,
214
                    service=self.service)
215
        elif self.service.name == 'CAMSP':
216
            return list_acts.list_acts_for_billing_CAMSP(self.start_date,
217
                    self.end_date, service=self.service)
218
        elif 'SESSAD' in self.service.name:
219
            return list_acts.list_acts_for_billing_SESSAD(self.start_date,
220
                    self.end_date, service=self.service)
221
        else:
222
            raise RuntimeError('Unknown service', self.service)
223

    
224

    
225
    def get_stats_or_validate(self, commit=False):
226
        '''
227
            If the invoicing is in state open or closed and commit is False
228
                Return the stats of the billing
229
            If the invoicing is in state open or closed and commit is True
230
                Proceed to invoices creation, healthcare charging, acts as billed
231
                Return the stats of the billing
232
            If the invoicing is in state validated or sent
233
                Return the stats of the billing
234
        '''
235
        days_not_locked = 0
236
        if self.service.name == 'CMPP':
237
            if self.status in (Invoicing.STATUS.open,
238
                    Invoicing.STATUS.closed):
239
                '''
240
                    The stats are build dynamiccaly according to the
241
                    acts billable and the healthcares
242
                '''
243
                (acts_not_locked, days_not_locked, acts_not_valide,
244
                    acts_not_billable, acts_pause, acts_diagnostic,
245
                    acts_treatment, acts_losts,
246
                    acts_losts_missing_policy) = \
247
                        self.list_for_billing()
248

    
249
                (invoices, len_invoices, len_invoices_hors_pause,
250
                    len_acts_invoiced, len_acts_invoiced_hors_pause) = \
251
                        build_invoices_from_acts(acts_diagnostic, acts_treatment)
252
                len_patient_invoiced = len(invoices.keys())
253
                len_patient_invoiced_hors_pause = 0
254
                for patient in invoices.keys():
255
                    if not patient.pause:
256
                        len_patient_invoiced_hors_pause = len_patient_invoiced_hors_pause + 1
257

    
258
                patients = set(acts_not_locked.keys() + acts_not_valide.keys() + \
259
                    acts_not_billable.keys() + acts_diagnostic.keys() + acts_treatment.keys() + \
260
                    acts_losts.keys() + acts_pause.keys() + acts_losts_missing_policy.keys())
261

    
262
                patients_stats = []
263
                len_patient_with_lost_acts = 0
264
                len_acts_lost = 0
265
                len_patient_acts_paused = 0
266
                len_acts_paused = 0
267
                len_patient_with_lost_acts_missing_policy = 0
268
                len_acts_losts_missing_policy = 0
269
                batches = {}
270
                last_batch = {}
271
                for patient in patients:
272
                    dic = {}
273
                    if patient in invoices.keys():
274
                        dic['invoices'] = invoices[patient]
275
                        if commit and not patient.pause:
276
                            policy_holder = patient.policyholder
277
                            try:
278
                                address = unicode(policy_holder.addresses.filter(place_of_life=True)[0])
279
                            except:
280
                                try:
281
                                    address = unicode(policy_holder.addresses.all()[0])
282
                                except:
283
                                    address = u''
284
                            invoice_kwargs = dict(
285
                                    patient_id=patient.id,
286
                                    patient_last_name=patient.last_name,
287
                                    patient_first_name=patient.first_name,
288
                                    patient_social_security_id=patient.social_security_id or '',
289
                                    patient_birthdate=patient.birthdate,
290
                                    patient_twinning_rank=patient.twinning_rank,
291
                                    patient_healthcenter=patient.health_center,
292
                                    patient_other_health_center=patient.other_health_center or '',
293
                                    patient_entry_date=patient.entry_date,
294
                                    patient_exit_date=patient.exit_date,
295
                                    policy_holder_id=policy_holder.id,
296
                                    policy_holder_last_name=policy_holder.last_name,
297
                                    policy_holder_first_name=policy_holder.first_name,
298
                                    policy_holder_social_security_id=policy_holder.social_security_id,
299
                                    policy_holder_other_health_center=policy_holder.other_health_center or '',
300
                                    policy_holder_healthcenter=policy_holder.health_center,
301
                                    policy_holder_address=address)
302
                            for invoice in invoices[patient]:
303
                                ppa = invoice['ppa'] * 100
304
                                acts = invoice['acts']
305
                                amount = ppa * len(acts)
306
                                in_o = Invoice(
307
                                        invoicing=self,
308
                                        ppa=ppa,
309
                                        amount=amount,
310
                                        **invoice_kwargs)
311
                                in_o.save()
312
                                for act, hc in acts:
313
                                    act.is_billed = True
314
                                    act.healthcare = hc
315
                                    act.save()
316
                                    in_o.acts.add(act)
317

    
318
                                # calcule le numero de lot pour cette facture
319
                                hc = in_o.health_center
320
                                hc_dest = hc.hc_invoice or hc
321
                                dest = hc_dest.large_regime.code + ' ' + hc_dest.dest_organism
322
                                if dest not in batches:
323
                                    batches[dest] = {}
324
                                if hc not in batches[dest]:
325
                                    nb1 = Invoice.objects.new_batch_number(health_center=hc_dest, invoicing=self)
326
                                    nb2 = Invoice.objects.new_batch_number(health_center=hc, invoicing=self)
327
                                    nb = max(nb1, nb2, last_batch.get(dest,0) + 1)
328
                                    last_batch[dest] = nb
329
                                    batches[dest][hc] = [{ 'batch': nb, 'size': 2, 'invoices': [], }]
330
                                # pas plus de 950 lignes par lot (le fichier B2 final ne doit
331
                                # pas depasser 999 lignes au total) :
332
                                b = batches[dest][hc].pop()
333
                                b2_size = in_o.acts.count() + 2
334
                                if (b['size'] + b2_size) > 950:
335
                                    batches[dest][hc].append(b)
336
                                    nb = last_batch.get(dest, 0) + 1
337
                                    last_batch[dest] = nb
338
                                    b = {'batch': nb, 'size': 2, 'invoices': []}
339
                                b['invoices'].append(in_o)
340
                                b['size'] += b2_size
341
                                batches[dest][hc].append(b)
342

    
343
                                in_o.batch = b['batch']
344
                                in_o.save()
345

    
346
                            pass
347
                    if patient in acts_losts.keys():
348
                        # TODO: More details about healthcare
349
                        dic['losts'] = acts_losts[patient]
350
                        len_patient_with_lost_acts = len_patient_with_lost_acts + 1
351
                        len_acts_lost = len_acts_lost + len(acts_losts[patient])
352
                    if patient in acts_pause.keys():
353
                        dic['acts_paused'] = acts_pause[patient]
354
                        len_patient_acts_paused = len_patient_acts_paused + 1
355
                        len_acts_paused = len_acts_paused + len(acts_pause[patient])
356
                    if patient in acts_losts_missing_policy.keys():
357
                        # TODO: More details about healthcare
358
                        dic['losts_missing_policy'] = acts_losts_missing_policy[patient]
359
                        len_patient_with_lost_acts_missing_policy = len_patient_with_lost_acts_missing_policy + 1
360
                        len_acts_losts_missing_policy = len_acts_losts_missing_policy + len(acts_losts_missing_policy[patient])
361
                    patients_stats.append((patient, dic))
362
                patients_stats = sorted(patients_stats, key=lambda patient: (patient[0].last_name, patient[0].first_name))
363

    
364
                len_patients = len(patients_stats)
365

    
366
                if commit:
367
                    self.status = Invoicing.STATUS.validated
368
                    self.save()
369

    
370
            else:
371
                '''
372
                    Grab stats from the invoices
373
                '''
374
                len_patients = 0
375
                len_invoices = 0
376
                len_acts_invoiced = 0
377
                patients_stats = {}
378
                days_not_locked = []
379
                invoices = self.invoice_set.all()
380
                patients = {}
381
                for invoice in invoices:
382
                    len_invoices = len_invoices + 1
383
                    len_acts_invoiced = len_acts_invoiced + len(invoice.acts.all())
384
                    patient = None
385
                    if not invoice.patient_id in patients.keys():
386
                        try:
387
                            patient = PatientRecord.objects.get(id=invoice.patient_id)
388
                        except:
389
                            patient = PatientRecord(last_name=patient_last_name, first_name=patient_first_name)
390
                        patients[invoice.patient_id] = patient
391
                        len_patients = len_patients + 1
392
                        patients_stats[patient] = {}
393
                        patients_stats[patient]['invoices'] = []
394
                    else:
395
                        patient = patients[invoice.patient_id]
396
                    patients_stats[patient]['invoices'].append(invoice)
397
                patients_stats = sorted(patients_stats.items(), key=lambda patient: (patient[0].last_name, patient[0].first_name))
398
                # all patients in the invoicing are invoiced
399
                len_patient_invoiced = 0
400
                # These stats are not necessary because excluded form the validated invoicing
401
                len_invoices_hors_pause = 0
402
                len_acts_invoiced_hors_pause = 0
403
                len_patient_invoiced_hors_pause = 0
404
                len_acts_lost = 0
405
                len_patient_with_lost_acts = 0
406
                len_patient_acts_paused = 0
407
                len_acts_paused = 0
408
                len_patient_with_lost_acts_missing_policy = 0
409
                len_acts_losts_missing_policy = 0
410
            return (len_patients, len_invoices, len_invoices_hors_pause,
411
                len_acts_invoiced, len_acts_invoiced_hors_pause,
412
                len_patient_invoiced, len_patient_invoiced_hors_pause,
413
                len_acts_lost, len_patient_with_lost_acts, patients_stats,
414
                days_not_locked, len_patient_acts_paused,
415
                len_acts_paused, len_acts_losts_missing_policy,
416
                len_patient_with_lost_acts_missing_policy)
417
        elif self.service.name == 'CAMSP':
418
            if self.status in Invoicing.STATUS.closed:
419
                (acts_not_locked, days_not_locked, acts_not_valide,
420
                acts_not_billable, acts_pause, acts_bad_state,
421
                acts_accepted) = self.list_for_billing()
422
                len_patient_pause = 0
423
                len_patient_hors_pause = 0
424
                len_acts_pause = 0
425
                len_acts_hors_pause = 0
426
                len_patient_acts_paused = 0
427
                len_acts_paused = 0
428
                patients = set(acts_accepted.keys() + acts_pause.keys())
429
                patients_stats = []
430
                for patient in patients:
431
                    dic = {}
432
                    if patient in acts_accepted.keys():
433
                        acts = acts_accepted[patient]
434
                        dic['accepted'] = acts
435
                        if patient.pause:
436
                            len_patient_pause = len_patient_pause + 1
437
                            len_acts_pause = len_acts_pause + len(acts)
438
                        else:
439
                            len_patient_hors_pause = len_patient_hors_pause + 1
440
                            len_acts_hors_pause = len_acts_hors_pause + len(acts)
441
                            if commit:
442
                                for act in acts:
443
                                    self.acts.add(act)
444
                    if patient in acts_pause.keys():
445
                        dic['acts_paused'] = acts_pause[patient]
446
                        len_patient_acts_paused = len_patient_acts_paused + 1
447
                        len_acts_paused = len_acts_paused + len(acts_pause[patient])
448
                    patients_stats.append((patient, dic))
449
                patients_stats = sorted(patients_stats, key=lambda patient: (patient[0].last_name, patient[0].first_name))
450
                if commit:
451
                    self.status = Invoicing.STATUS.validated
452
                    self.save()
453
            else:
454
                patients_stats = {}
455
                len_patient_pause = 0
456
                len_patient_hors_pause = 0
457
                len_acts_pause = 0
458
                len_acts_hors_pause = 0
459
                len_patient_acts_paused = 0
460
                len_acts_paused = 0
461
                days_not_locked = []
462
                for act in self.acts.all():
463
                    if act.patient in patients_stats.keys():
464
                        patients_stats[act.patient]['accepted'].append(act)
465
                        len_acts_hors_pause = len_acts_hors_pause + 1
466
                    else:
467
                        len_patient_hors_pause = len_patient_hors_pause + 1
468
                        len_acts_hors_pause = len_acts_hors_pause + 1
469
                        patients_stats[act.patient] = {}
470
                        patients_stats[act.patient]['accepted'] = [act]
471
                patients_stats = sorted(patients_stats.items(), key=lambda patient: (patient[0].last_name, patient[0].first_name))
472
            return (len_patient_pause, len_patient_hors_pause,
473
                len_acts_pause, len_acts_hors_pause, patients_stats,
474
                days_not_locked, len_patient_acts_paused,
475
                len_acts_paused)
476
        else:
477
            if self.status in Invoicing.STATUS.closed:
478
                (acts_not_locked, days_not_locked, acts_not_valide,
479
                acts_not_billable, acts_pause, acts_bad_state,
480
                acts_missing_valid_notification, acts_accepted) = \
481
                    self.list_for_billing()
482

    
483
                len_patient_pause = 0
484
                len_patient_hors_pause = 0
485
                len_acts_pause = 0
486
                len_acts_hors_pause = 0
487
                len_patient_acts_paused = 0
488
                len_acts_paused = 0
489
                len_patient_missing_notif = 0
490
                len_acts_missing_notif = 0
491
                patients = set(acts_accepted.keys() + acts_pause.keys())
492
                patients_stats = []
493
                for patient in patients:
494
                    dic = {}
495
                    if patient in acts_accepted.keys():
496
                        acts = acts_accepted[patient]
497
                        dic['accepted'] = acts
498
                        if patient.pause:
499
                            len_patient_pause = len_patient_pause + 1
500
                            len_acts_pause = len_acts_pause + len(acts)
501
                        else:
502
                            len_patient_hors_pause = len_patient_hors_pause + 1
503
                            len_acts_hors_pause = len_acts_hors_pause + len(acts)
504
                            if commit:
505
                                for act in acts:
506
                                    self.acts.add(act)
507
                    if patient in acts_missing_valid_notification.keys():
508
                        acts = acts_missing_valid_notification[patient]
509
                        dic['missings'] = acts
510
                        len_patient_missing_notif = len_patient_missing_notif + 1
511
                        len_acts_missing_notif = len_acts_missing_notif + len(acts)
512
                        if not 'accepted' in dic:
513
                            len_patient_hors_pause = len_patient_hors_pause + 1
514
                        if commit:
515
                            for act in acts:
516
                                self.acts.add(act)
517
                    if patient in acts_pause.keys():
518
                        dic['acts_paused'] = acts_pause[patient]
519
                        len_patient_acts_paused = len_patient_acts_paused + 1
520
                        len_acts_paused = len_acts_paused + len(acts_pause[patient])
521
                    patients_stats.append((patient, dic))
522
                patients_stats = sorted(patients_stats, key=lambda patient: (patient[0].last_name, patient[0].first_name))
523
                len_acts_hors_pause = len_acts_hors_pause + len_acts_missing_notif
524
                if commit:
525
                    self.status = Invoicing.STATUS.validated
526
                    self.save()
527
            else:
528
                patients_stats = {}
529
                len_patient_pause = 0
530
                len_patient_hors_pause = 0
531
                len_acts_pause = 0
532
                len_acts_hors_pause = 0
533
                len_patient_acts_paused = 0
534
                len_acts_paused = 0
535
                len_patient_missing_notif = 0
536
                len_acts_missing_notif = 0
537
                days_not_locked = []
538
                for act in self.acts.all():
539
                    if act.patient in patients_stats.keys():
540
                        patients_stats[act.patient]['accepted'].append(act)
541
                        len_acts_hors_pause = len_acts_hors_pause + 1
542
                    else:
543
                        len_patient_hors_pause = len_patient_hors_pause + 1
544
                        len_acts_hors_pause = len_acts_hors_pause + 1
545
                        patients_stats[act.patient] = {}
546
                        patients_stats[act.patient]['accepted'] = [act]
547
                patients_stats = sorted(patients_stats.items(), key=lambda patient: (patient[0].last_name, patient[0].first_name))
548
            return (len_patient_pause, len_patient_hors_pause,
549
                len_acts_pause, len_acts_hors_pause,
550
                len_patient_missing_notif, len_acts_missing_notif,
551
                patients_stats, days_not_locked,
552
                len_patient_acts_paused, len_acts_paused)
553

    
554
    def save(self, *args, **kwargs):
555
        if not self.seq_id:
556
            self.seq_id = self.allocate_seq_id()
557
        super(Invoicing, self).save(*args, **kwargs)
558

    
559
    def close(self, end_date=None):
560
        '''Close an open invoicing'''
561
        if self.service.name != 'CMPP':
562
            raise RuntimeError('closing Invoicing is only for the CMPP')
563
        if self.status != Invoicing.STATUS.open:
564
            raise RuntimeError('closing an un-opened Invoicing')
565
        if not end_date:
566
            today = date.today()
567
            end_date = date(day=today.day, month=today.month, year=today.year)
568
            if end_date < self.start_date:
569
                end_date = self.start_date + relativedelta(days=1)
570
        self.status = Invoicing.STATUS.closed
571
        self.end_date = end_date
572
        self.save()
573
        start_date = self.end_date + relativedelta(days=1)
574
        invoicing = Invoicing(service=self.service,
575
            start_date=start_date,
576
            status=Invoicing.STATUS.open)
577
        invoicing.save()
578
        return invoicing
579

    
580
    def export_for_accounting(self):
581
        '''
582
            Il faut separer les ecritures pour l'année en cours et les années
583
            passées.
584
        '''
585
        def add_ecriture(health_center, amount):
586
            accounting_number = '0' + health_center.accounting_number[1:]
587
            '''Le nom du compte ne devrait pas etre important et seul compter
588
            le numéro de compte'''
589
            name = health_center.name
590
            if len(name) > 30:
591
                name = name[0:30]
592
            ecriture = progor.EcritureComptable(date, accounting_number,
593
                name, cycle, debit=amount)
594
            imputation = progor.ImputationAnalytique(debit=amount)
595
            ecriture.add_imputation(imputation)
596
            journal.add_ecriture(ecriture)
597
        journal = progor.IdentificationJournal()
598
        ecritures_current = dict()
599
        ecritures_past = dict()
600
        total_current = 0
601
        total_past = 0
602
        date = self.end_date.strftime("%d/%m/%Y")
603
        cycle = str(self.seq_id)
604
        invoice_year = datetime(self.end_date.year, 1, 1).date()
605
        for invoice in self.invoice_set.all():
606
            if invoice.end_date < invoice_year:
607
                ecritures_past.setdefault(invoice.health_center, 0)
608
                ecritures_past[invoice.health_center] += invoice.amount
609
                total_past += invoice.amount
610
            else:
611
                ecritures_current.setdefault(invoice.health_center, 0)
612
                ecritures_current[invoice.health_center] += invoice.amount
613
                total_current += invoice.amount
614
        '''Ce qui est facturé aux caisses est en débit'''
615
        for health_center, amount in ecritures_past.items():
616
            add_ecriture(health_center, amount)
617
        for health_center, amount in ecritures_current.items():
618
            add_ecriture(health_center, amount)
619
        '''On équilibre avec du produit en crédit'''
620
        #Produit années précédentes
621
        if total_past:
622
            ecriture = progor.EcritureComptable(date, '77200000',
623
                'PRODUITS EXERCICES ANTERIEURS', cycle, credit=total_past, type_compte='0')
624
            imputation = progor.ImputationAnalytique(credit=total_past)
625
            ecriture.add_imputation(imputation)
626
            journal.add_ecriture(ecriture)
627
        #Produit année en cours
628
        ecriture = progor.EcritureComptable(date, '73130000',
629
            'PRODUITS EXERCICE', cycle, credit=total_current, type_compte='0')
630
        imputation = progor.ImputationAnalytique(credit=total_current)
631
        ecriture.add_imputation(imputation)
632
        journal.add_ecriture(ecriture)
633
        content = journal.render()
634
        service = self.service
635
        now = datetime.now()
636
        output_file = tempfile.NamedTemporaryFile(prefix='%s-export-%s-' %
637
                (service.slug, self.id), suffix='-%s.txt' % now, delete=False)
638
        output_file.write(content)
639
        return output_file.name
640

    
641

    
642
#class PricePerAct(models.Model):
643
#    price = models.IntegerField()
644
#    start_date = models.DateField(
645
#            verbose_name=u"Prise d'effet",
646
#            default=date(day=5,month=1,year=1970))
647
#    end_date = models.DateField(
648
#            verbose_name=u"Fin d'effet",
649
#            blank=True,
650
#            null=True)
651

    
652
#    @classmethod
653
#    def get_price(cls, at_date=None):
654
#        if not at_date:
655
#            at_date = date.today()
656
#        if isinstance(at_date, datetime):
657
#            at_date = date(day=at_date.day, month=at_date.month,
658
#                year=at_date.year)
659
#        found = cls.objects.filter(start_date__lte = at_date).latest('start_date')
660
#        if not found:
661
#            raise Exception('No price to apply')
662
#        return found.price
663

    
664
#    def __unicode__(self):
665
#        val = str(self.price) + ' from ' + str(self.start_date)
666
#        if self.end_date:
667
#            val = val + ' to ' + str(self.end_date)
668
#        return val
669

    
670

    
671
#def add_price(price, start_date=None):
672
#    price_o = None
673
#    ppas = PricePerAct.objects.all()
674
#    if ppas:
675
#        if not start_date:
676
#            raise Exception('A start date is mandatory to add a new Price')
677
#        last_ppa = PricePerAct.objects.latest('start_date')
678
#        if last_ppa.start_date >= start_date:
679
#            raise Exception('The new price cannot apply before the price that currently applies.')
680
#        if last_ppa.end_date and last_ppa.end_date != (start_date + relativedelta(days=-1)):
681
#            raise Exception('The new price should apply the day after the last price ends.')
682
#        last_ppa.end_date = start_date + relativedelta(days=-1)
683
#        last_ppa.save()
684
#    if not start_date:
685
#        price_o = PricePerAct(price=price)
686
#    else:
687
#        price_o = PricePerAct(price=price, start_date=start_date)
688
#    price_o.save()
689
#    return price_o
690

    
691

    
692
PREVIOUS_MAX_BATCH_NUMBERS = {
693
    'CF00000004001110': 34,
694
    'CT00000001016421': 1,
695
    'CT00000001016422': 141,
696
    'CT00000001025691': 20,
697
    'CT00000002011011': 8,
698
    'CT00000002381421': 15,
699
    'MA00000002381421': 48,
700

    
701
    'SM00000091007381': 98, # faure 43
702
    'SM00000091007421': 98, # faure 44
703
    'SM00000091007422': 98, # faure 45
704
    'SM00000091007431': 98, # faure 46
705
    'SM00000091007691': 98, # faure 47
706
    'SM00000091007764': 98, # faure 48
707

    
708
    'SM00000092001422': 14,
709
    'SM00000092001691': 13,
710
    'SM00000099038939': 24,
711
}
712

    
713

    
714
class InvoiceManager(models.Manager):
715
    def new_batch_number(self, health_center, invoicing):
716
        '''Compute the next batch number for the given health center'''
717
        global PREVIOUS_MAX_BATCH_NUMBERS
718
        agg = self \
719
                .filter(invoicing__service=invoicing.service) \
720
                .filter(invoicing__seq_id__lt=invoicing.seq_id) \
721
                .filter(
722
                    Q(patient_healthcenter=health_center,
723
                        policy_holder_healthcenter__isnull=True)|
724
                    Q(policy_holder_healthcenter=health_center)) \
725
                        .aggregate(Max('batch'))
726
        max_bn = agg['batch__max']
727
        if max_bn is None:
728
            max_bn = PREVIOUS_MAX_BATCH_NUMBERS.get(health_center.b2_000(), 0)
729
        else:
730
            max_bn = max(max_bn, PREVIOUS_MAX_BATCH_NUMBERS.get(health_center.b2_000(), 0))
731
        return max_bn + 1
732

    
733

    
734
class Invoice(models.Model):
735
    objects = InvoiceManager()
736
    number = models.IntegerField(blank=True, null=True)
737
    batch = models.IntegerField(blank=True, null=True)
738
    # the patient can be modified (or even deleted) after invoice creation, so
739
    # we copy his informations here
740
    patient_id = models.IntegerField(blank=True, null=True)
741
    patient_last_name = models.CharField(max_length=128,
742
            verbose_name=u'Nom du patient', default='', blank=True)
743
    patient_first_name = models.CharField(max_length=128,
744
            verbose_name=u'Prénom(s) du patient', default='', blank=True)
745
    patient_social_security_id = models.CharField(max_length=13,
746
            verbose_name=u"NIR", default='', blank=True)
747
    patient_birthdate = models.DateField(verbose_name=u"Date de naissance",
748
            null=True, blank=True)
749
    patient_twinning_rank = models.IntegerField(
750
        verbose_name=u"Rang (gémellité)",
751
        null=True, blank=True)
752
    patient_healthcenter = models.ForeignKey('ressources.HealthCenter',
753
            verbose_name=u"Centre d'assurance maladie",
754
            related_name='related_by_patient_invoices',
755
            null=True, blank=True)
756
    patient_entry_date = models.DateField(verbose_name=u'Date d\'entrée du patient',
757
            blank=True, null=True)
758
    patient_exit_date = models.DateField(verbose_name=u'Date de sortie du patient',
759
            blank=True, null=True)
760
    patient_other_health_center = models.CharField(
761
        verbose_name=u"Centre spécifique", max_length=4, default='',
762
        blank=True)
763
    # policy holder informations
764
    policy_holder_id = models.IntegerField(blank=True, null=True)
765
    policy_holder_last_name = models.CharField(max_length=128,
766
            verbose_name=u'Nom de l\'assuré', default='', blank=True)
767
    policy_holder_first_name = models.CharField(max_length=128,
768
            verbose_name=u'Prénom(s) de l\' assuré', default='', blank=True)
769
    policy_holder_social_security_id = models.CharField(max_length=13,
770
            verbose_name=u"NIR de l\'assuré", default='', blank=True)
771
    policy_holder_healthcenter = models.ForeignKey('ressources.HealthCenter',
772
            verbose_name=u"Centre d'assurance maladie de l\'assuré",
773
            related_name='related_by_policy_holder_invoices',
774
            null=True, blank=True)
775
    policy_holder_other_health_center = models.CharField(
776
            verbose_name=u"Centre spécifique de l\'assuré", max_length=4,
777
            default='', blank=True)
778
    policy_holder_address = models.CharField(max_length=128,
779
            verbose_name=u'Adresse de l\'assuré', default='', blank=True)
780

    
781
    created = models.DateTimeField(u'Création', auto_now_add=True)
782
    invoicing = models.ForeignKey('facturation.Invoicing',
783
        on_delete='PROTECT')
784
    acts = models.ManyToManyField('actes.Act')
785
    amount = models.IntegerField()
786
    ppa = models.IntegerField()
787
    rejected = models.BooleanField(verbose_name=u"Rejeté", default=False)
788

    
789
    def save(self, *args, **kwargs):
790
        if not self.number:
791
            invoicing = self.invoicing
792
            self.number = invoicing.seq_id * 1000000 + 1
793
            max_number = invoicing.invoice_set.aggregate(Max('number'))['number__max']
794
            if max_number:
795
                self.number = max_number + 1
796
        super(Invoice, self).save(*args, **kwargs)
797

    
798
    @property
799
    def start_date(self):
800
        res = date.max
801
        for act in self.acts.all():
802
            res = min(res, act.date)
803
        return res
804

    
805
    @property
806
    def health_center(self):
807
        return self.policy_holder_healthcenter or self.patient_healthcenter
808

    
809
    @property
810
    def end_date(self):
811
        res = date.min
812
        for act in self.acts.all():
813
            res = max(res, act.date)
814
        return res
815

    
816
    @property
817
    def decimal_amount(self):
818
        return Decimal(self.amount) / Decimal(100)
819

    
820
    @property
821
    def decimal_ppa(self):
822
        return Decimal(self.ppa) / Decimal(100)
823

    
824
    @property
825
    def kind(self):
826
        tag = self.acts.all()[0].get_hc_tag()
827
        return tag[0]
828

    
829
    @property
830
    def patient_social_security_id_full(self):
831
        if self.patient_social_security_id:
832
            return social_security_id_full(self.patient_social_security_id)
833
        else:
834
            return ''
835

    
836
    @property
837
    def policy_holder_social_security_id_full(self):
838
        if self.policy_holder_social_security_id:
839
            return social_security_id_full(self.policy_holder_social_security_id)
840
        else:
841
            return ''
842

    
843
    def __unicode__(self):
844
        return "Facture %d de %d euros (%d actes)" % (self.number, self.amount, len(self.acts.all()))
(9-9/14)