Project

General

Profile

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

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

1
# -*- coding: utf-8 -*-
2

    
3
import datetime
4
import logging
5
import os
6
import os.path
7
import tempfile
8
import textwrap
9

    
10
from decimal import Decimal
11
from collections import defaultdict
12

    
13
from xhtml2pdf.pisa import CreatePDF
14
from django.template import loader, Context
15
from django.conf import settings
16

    
17
from invoice_template import InvoiceTemplate
18
from ..pdftk import PdfTk
19
from batches import build_batches
20
from calebasse.utils import get_nir_control_key
21

    
22
logger = logging.getLogger(__name__)
23

    
24
class Counter(object):
25
    def __init__(self, initial_value=0):
26
        self.counter = initial_value
27

    
28
    def increment(self):
29
        self.counter += 1
30
        return (self.counter-1)
31

    
32
    def __str__(self):
33
        return str(self.counter)
34

    
35

    
36
def render_to_pdf_file(templates, ctx, prefix='tmp', delete=False):
37
    temp = tempfile.NamedTemporaryFile(prefix=prefix, suffix='.pdf',
38
            delete=False)
39
    try:
40
        t = loader.select_template(templates)
41
        html = t.render(Context(ctx))
42
        CreatePDF(html, temp)
43
        temp.flush()
44
        return temp.name
45
    except:
46
        if delete:
47
            try:
48
                os.unlink(temp.name)
49
            except:
50
                pass
51
        raise
52

    
53

    
54
def price_details(service, invoicing,
55
                  header_service_template = 'facturation/bordereau-%s.html',
56
                  header_template = 'facturation/prices-details.html',
57
                  delete = False):
58
    context = {'invoicings': invoicing.get_stats_per_price_per_year()}
59
    return render_to_pdf_file((header_service_template % service.slug,
60
                               header_template),
61
                              context,
62
                              delete = delete)
63

    
64

    
65
def header_file(service, invoicing, health_center, batches,
66
        header_service_template='facturation/bordereau-%s.html',
67
        header_template='facturation/bordereau.html',
68
        delete=False,
69
        counter=None):
70
    synthesis = {
71
            'total': sum(batch.total for batch in batches),
72
            'number_of_acts': sum(batch.number_of_acts for batch in batches),
73
            'number_of_invoices': sum(batch.number_of_invoices for batch in batches),
74
    }
75
    ctx = {
76
            'now': datetime.datetime.now(),
77
            'health_center': health_center,
78
            'service': service,
79
            'batches': batches,
80
            'synthesis': synthesis,
81
            'counter': counter,
82
    }
83

    
84
    prefix = '%s-invoicing-%s-healthcenter-%s-' % (
85
            service.slug, invoicing.id, health_center.id)
86
    return render_to_pdf_file(
87
            (header_service_template % service.slug,
88
            header_template), ctx, prefix=prefix, delete=delete)
89

    
90

    
91
def invoice_files(service, invoicing, batch, invoice, counter=None):
92
    template_path = os.path.join(
93
            os.path.dirname(__file__),
94
            'static',
95
            'facturation',
96
            'invoice.pdf')
97
    tpl = InvoiceTemplate(
98
            template_path=template_path,
99
            prefix='%s-invoicing-%s-invoice-%s-'
100
                % ( service.slug, invoicing.id, invoice.id),
101
            suffix='-%s.pdf' % datetime.datetime.now())
102
    health_center = invoice.health_center
103
    code_organisme = u'%s - %s %s' % (
104
      health_center.large_regime.code,
105
      health_center.dest_organism,
106
      health_center.name)
107
    address = textwrap.fill(invoice.policy_holder_address, 40)
108
    if not invoice.first_tag:
109
        subtitle = 'INDEFINI'
110
    elif invoice.first_tag[0] == 'D':
111
        subtitle = 'DIAGNOSTIC'
112
    else:
113
        subtitle = 'TRAITEMENT'
114
    ctx = {
115
            'NUM_FINESS': '420788606',
116
            'NUM_LOT': unicode(batch.number),
117
            'NUM_FACTURE': unicode(invoice.number),
118
            'NUM_ENTREE': unicode(invoice.patient_id),
119
            'IDENTIFICATION_ETABLISSEMENT': '''%s SAINT ETIENNE
120
66/68, RUE MARENGO
121
42000 SAINT ETIENNE''' % service.name,
122
            'DATE_ELABORATION': datetime.datetime.now().strftime('%d/%m/%Y'),
123
            'NOM_BENEFICIAIRE': u' '.join((invoice.patient_first_name,
124
                invoice.patient_last_name.upper())),
125
            'NIR_BENEFICIAIRE': invoice.patient_social_security_id_full,
126
            'DATE_NAISSANCE_RANG': u' '.join(
127
                (unicode(invoice.patient_birthdate),
128
                    unicode(invoice.patient_twinning_rank))),
129
            'CODE_ORGANISME':  code_organisme,
130
            'ABSENCE_SIGNATURE': True,
131
            'ADRESSE_ASSURE': address,
132
            'SUBTITLE': subtitle,
133
            'PART_OBLIGATOIRE': True,
134
            'PART_COMPLEMENTAIRE': True,
135
    }
136
    if counter is not None:
137
        ctx['COUNTER'] = counter.increment()
138
    if invoice.patient_entry_date is not None:
139
        ctx['DATE_ENTREE'] = invoice.patient_entry_date.strftime('%d/%m/%Y')
140
    if invoice.patient_exit_date is not None:
141
        ctx['DATE_SORTIE'] = invoice.patient_exit_date.strftime('%d/%m/%Y')
142
    if invoice.policy_holder_id != invoice.patient_id:
143
        ctx.update({
144
                'NOM_ASSURE': u' '.join((
145
                    invoice.policy_holder_first_name,
146
                    invoice.policy_holder_last_name.upper())),
147
                'NIR_ASSURE': invoice.policy_holder_social_security_id_full,
148
            })
149
    total1 = Decimal(0)
150
    total2 = Decimal(0)
151
    tableau1 = []
152
    tableau2 = []
153
    dates = []
154
    if invoice.list_dates:
155
        dates = invoice.list_dates.split('$')
156
    if dates:
157
        if len(dates) > 30:
158
            raise RuntimeError('Too much acts in invoice %s' % invoice.id)
159
        kind = 'X'
160
        offset = 0
161
        prestation = u'X'
162
        if invoice.first_tag:
163
            kind = invoice.first_tag[0]
164
            offset = int(invoice.first_tag[1:])
165
            prestation = u'SNS' if kind == 'T' else u'SD'
166
        for date in dates[:15]:
167
            tableau1.append([u'19', u'320', prestation, date, date,
168
                invoice.decimal_ppa, 1, invoice.decimal_ppa, kind + str(offset)])
169
            total1 += invoice.decimal_ppa
170
            offset += 1
171
        for date in dates[15:30]:
172
            tableau2.append([u'19', u'320', prestation, date, date,
173
                invoice.decimal_ppa, 1, invoice.decimal_ppa, kind + str(offset)])
174
            total2 += invoice.decimal_ppa
175
            offset += 1
176
    ctx.update({
177
            'TABLEAU1': tableau1,
178
            'TABLEAU2': tableau2,
179
        })
180
    ctx['SOUS_TOTAL1'] = total1
181
    if total2 != Decimal(0):
182
        ctx['SOUS_TOTAL2'] = total2
183
    ctx['TOTAL'] = invoice.decimal_amount
184

    
185
    try:
186
        repeat = settings.BATCH_CONTENT_TIMES_IN_INVOINCING_FILE
187
    except:
188
        repeat = 1
189

    
190
    output = tpl.generate(ctx)
191
    return [output for i in xrange(repeat)]
192

    
193
def render_not_cmpp_header(invoicing):
194
    header_template='facturation/bordereau_not_cmpp_header.html'
195
    management_codes = dict()
196
    total_acts = 0
197
    for invoice in invoicing.invoice_set.all():
198
        total_acts += invoice.acts.count()
199
        if invoice.policy_holder_management_code:
200
            management_codes.setdefault(invoice.policy_holder_management_code, []).append(invoice)
201
    list_management_codes = list()
202
    for mc, invoices in management_codes.iteritems():
203
        dic = dict()
204
        dic['code'] = mc
205
        dic['title'] = invoices[0].policy_holder_management_code_name
206
        dic['nb_files'] = len(invoices)
207
        nb_acts = 0
208
        for invoice in invoices:
209
            nb_acts += invoice.acts.count()
210
        dic['nb_acts'] = nb_acts
211
        list_management_codes.append(dic)
212
    list_management_codes.sort(key=lambda dic: dic['code'])
213
    ctx = {
214
            'now': datetime.datetime.now(),
215
            'service': invoicing.service,
216
            'start_date': invoicing.start_date,
217
            'end_date': invoicing.end_date,
218
            'list_management_codes': list_management_codes,
219
            'total_files': invoicing.invoice_set.count(),
220
            'total_acts': total_acts
221
    }
222
    prefix = '%s-invoicing-header-%s' % (
223
            invoicing.service.slug, invoicing.id)
224
    return render_to_pdf_file(
225
            (header_template, ), ctx, prefix=prefix, delete=True)
226

    
227
def render_not_cmpp_content(invoicing):
228
    header_template='facturation/bordereau_not_cmpp_content.html'
229
    total_acts = 0
230
    list_patients = list()
231
    for invoice in invoicing.invoice_set.all():
232
        total_acts += invoice.acts.count()
233
        policy_holder = ''
234
        if invoice.policy_holder_last_name:
235
            policy_holder = invoice.policy_holder_last_name.upper()
236
            if invoice.policy_holder_first_name:
237
                policy_holder += ' ' + invoice.policy_holder_first_name
238
        nir = None
239
        if invoice.policy_holder_social_security_id:
240
            nir = invoice.policy_holder_social_security_id + ' ' + str(get_nir_control_key(invoice.policy_holder_social_security_id))
241
        health_center = ''
242
        tp = ''
243
        cai = ''
244
        if invoice.policy_holder_healthcenter:
245
            health_center = invoice.policy_holder_healthcenter.name
246
            if invoice.policy_holder_healthcenter.large_regime:
247
                tp = invoice.policy_holder_healthcenter.large_regime.code
248
            cai = invoice.policy_holder_healthcenter.health_fund
249
        name = ''
250
        if invoice.patient_last_name:
251
            name = invoice.patient_last_name.upper()
252
            if invoice.patient_first_name:
253
                name += ' ' + invoice.patient_first_name
254
        list_patients.append({\
255
              'code' : invoice.policy_holder_management_code,
256
              'policy_holder': policy_holder,
257
              'nir': nir,
258
              'health_center': health_center,
259
              'tp': tp,
260
              'cai': cai,
261
              'cen': invoice.policy_holder_other_health_center,
262
              'number': invoice.patient_id,
263
              'name': name,
264
              'birth_date': invoice.patient_birthdate,
265
              'inscription_date': invoice.patient_entry_date,
266
              'sortie_date': invoice.patient_exit_date,
267
              'nb_actes': invoice.acts.count()})
268
    list_patients.sort(key=lambda dic: dic['policy_holder'])
269
    ctx = {
270
            'now': datetime.datetime.now(),
271
            'service': invoicing.service,
272
            'start_date': invoicing.start_date,
273
            'end_date': invoicing.end_date,
274
            'total_files': invoicing.invoice_set.count(),
275
            'total_acts': total_acts,
276
            'patients': list_patients
277
    }
278
    prefix = '%s-invoicing-content-%s' % (
279
            invoicing.service.slug, invoicing.id)
280
    return render_to_pdf_file(
281
            (header_template, ), ctx, prefix=prefix, delete=True)
282

    
283

    
284
def render_invoicing(invoicing, delete=False, headers=True, invoices=True):
285
    service = invoicing.service
286
    now = datetime.datetime.now()
287
    output_file = None
288
    all_files = [price_details(service, invoicing)]
289
    try:
290
        if service.name == 'CMPP':
291
            batches_by_health_center = build_batches(invoicing)
292
            centers = sorted(batches_by_health_center.keys())
293
            counter = Counter(1)
294
            for center in centers:
295
                if headers is not True and headers is not False and headers != center:
296
                    continue
297
                for batch in batches_by_health_center[center]:
298
                    files = batches_files(service, invoicing, center,
299
                        [batch], delete=delete,
300
                        headers=headers, invoices=invoices, counter=counter)
301
                    all_files.extend(files)
302
        else:
303
            header = render_not_cmpp_header(invoicing)
304
            all_files.append(header)
305
            content = render_not_cmpp_content(invoicing)
306
            all_files.append(content)
307
        output_file = None
308
        if settings.INVOICING_DIRECTORY and not os.path.exists(settings.INVOICING_DIRECTORY):
309
            logger.warning("INVOICING_DIRECTORY %r doesn't exist" % settings.INVOICING_DIRECTORY)
310
        elif settings.INVOICING_DIRECTORY:
311
            to_path = os.path.join(settings.INVOICING_DIRECTORY, service.name)
312
            if not os.path.exists(to_path):
313
                os.makedirs(to_path)
314
            to_path = os.path.join(to_path, '%s-facturation-%s-%s.pdf' \
315
                % (service.slug, invoicing.seq_id, now.strftime('%d%m%Y-%H%M')))
316
            output_file = open(to_path, 'w')
317
        if not output_file:
318
            output_file = tempfile.NamedTemporaryFile(prefix='%s-invoicing-%s-' %
319
                    (service.slug, invoicing.id), suffix='-%s.pdf' % now, delete=False)
320
        pdftk = PdfTk()
321
        pdftk.concat(all_files, output_file.name)
322
        return output_file.name
323
    except:
324
        if delete and output_file:
325
            try:
326
                os.unlink(output_file.name)
327
            except:
328
                pass
329
        raise
330
    finally:
331
        # eventual cleanup
332
        if delete:
333
            for path in all_files:
334
                try:
335
                    os.unlink(path)
336
                except:
337
                    pass
338

    
339

    
340
def batches_files(service, invoicing, health_center, batches, delete=False,
341
        headers=True, invoices=True, counter=None):
342
    files = []
343
    try:
344
        if headers:
345
            header = header_file(service, invoicing, health_center, batches,
346
                                 delete=delete, counter=counter)
347

    
348
            try:
349
                repeat = settings.BATCH_HEADER_TIMES_IN_INVOICING_FILE
350
            except:
351
                repeat = 1
352
            map(lambda el: files.append(header), xrange(repeat))
353

    
354
        if invoices:
355
            for batch in batches:
356
                for invoice in batch.invoices:
357
                    # if invoices is a sequence, skip unlisted invoices
358
                    if invoices is not True and invoice not in invoices:
359
                        continue
360

    
361
                    files.extend(invoice_files(service, invoicing, batch, invoice, counter=counter))
362
        return files
363
    except:
364
        # cleanup
365
        if delete:
366
            for path in files:
367
                try:
368
                    os.unlink(path)
369
                except:
370
                    pass
371
        raise
(6-6/16)