Project

General

Profile

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

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

1 591ca598 Benjamin Dauvergne
# -*- coding: utf-8 -*-
2 0d5f763c Jérôme Schneider
3
import datetime
4
import logging
5 fa500e83 Benjamin Dauvergne
import os
6 591ca598 Benjamin Dauvergne
import os.path
7
import tempfile
8 2584bbc9 Benjamin Dauvergne
import textwrap
9 0d5f763c Jérôme Schneider
10 591ca598 Benjamin Dauvergne
from decimal import Decimal
11
from collections import defaultdict
12
13
from xhtml2pdf.pisa import CreatePDF
14 fa500e83 Benjamin Dauvergne
from django.template import loader, Context
15 93844deb Mikaël Ates
from django.conf import settings
16 591ca598 Benjamin Dauvergne
17
from invoice_template import InvoiceTemplate
18
from ..pdftk import PdfTk
19 ceb458a3 Benjamin Dauvergne
from batches import build_batches
20 2f2a7de9 Mikaël Ates
from calebasse.utils import get_nir_control_key
21 80f2dbfe Benjamin Dauvergne
22 0d5f763c Jérôme Schneider
logger = logging.getLogger(__name__)
23 c743d1bf Mikaël Ates
24 80f2dbfe Benjamin Dauvergne
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 591ca598 Benjamin Dauvergne
35 fa500e83 Benjamin Dauvergne
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 81e91620 Serghei MIHAI
54 0f05168e Serghei MIHAI
def price_details(service, invoicing,
55
                  header_service_template = 'facturation/bordereau-%s.html',
56
                  header_template = 'facturation/prices-details.html',
57
                  delete = False):
58 2ef43cdf Serghei MIHAI
    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 fa500e83 Benjamin Dauvergne
65
def header_file(service, invoicing, health_center, batches,
66
        header_service_template='facturation/bordereau-%s.html',
67
        header_template='facturation/bordereau.html',
68 80f2dbfe Benjamin Dauvergne
        delete=False,
69
        counter=None):
70 591ca598 Benjamin Dauvergne
    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 fa500e83 Benjamin Dauvergne
            'now': datetime.datetime.now(),
77
            'health_center': health_center,
78 591ca598 Benjamin Dauvergne
            'service': service,
79
            'batches': batches,
80
            'synthesis': synthesis,
81 80f2dbfe Benjamin Dauvergne
            'counter': counter,
82 591ca598 Benjamin Dauvergne
    }
83 2ef43cdf Serghei MIHAI
84 fa500e83 Benjamin Dauvergne
    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 591ca598 Benjamin Dauvergne
90 fa500e83 Benjamin Dauvergne
91 80f2dbfe Benjamin Dauvergne
def invoice_files(service, invoicing, batch, invoice, counter=None):
92 591ca598 Benjamin Dauvergne
    template_path = os.path.join(
93
            os.path.dirname(__file__),
94
            'static',
95
            'facturation',
96
            'invoice.pdf')
97 fa500e83 Benjamin Dauvergne
    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 7c757f94 Benjamin Dauvergne
    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 2584bbc9 Benjamin Dauvergne
    address = textwrap.fill(invoice.policy_holder_address, 40)
108 0074915c Mikaël Ates
    if not invoice.first_tag:
109
        subtitle = 'INDEFINI'
110
    elif invoice.first_tag[0] == 'D':
111 80f2dbfe Benjamin Dauvergne
        subtitle = 'DIAGNOSTIC'
112
    else:
113
        subtitle = 'TRAITEMENT'
114 7c757f94 Benjamin Dauvergne
    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 591ca598 Benjamin Dauvergne
66/68, RUE MARENGO
121 7c757f94 Benjamin Dauvergne
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 80f2dbfe Benjamin Dauvergne
                invoice.patient_last_name.upper())),
125 18889f61 Benjamin Dauvergne
            'NIR_BENEFICIAIRE': invoice.patient_social_security_id_full,
126 7c757f94 Benjamin Dauvergne
            '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 80f2dbfe Benjamin Dauvergne
            'ADRESSE_ASSURE': address,
132
            'SUBTITLE': subtitle,
133 73aae71e Benjamin Dauvergne
            'PART_OBLIGATOIRE': True,
134
            'PART_COMPLEMENTAIRE': True,
135 7c757f94 Benjamin Dauvergne
    }
136 80f2dbfe Benjamin Dauvergne
    if counter is not None:
137
        ctx['COUNTER'] = counter.increment()
138 fa500e83 Benjamin Dauvergne
    if invoice.patient_entry_date is not None:
139 7c757f94 Benjamin Dauvergne
        ctx['DATE_ENTREE'] = invoice.patient_entry_date.strftime('%d/%m/%Y')
140 fa500e83 Benjamin Dauvergne
    if invoice.patient_exit_date is not None:
141 7c757f94 Benjamin Dauvergne
        ctx['DATE_SORTIE'] = invoice.patient_exit_date.strftime('%d/%m/%Y')
142 80f2dbfe Benjamin Dauvergne
    if invoice.policy_holder_id != invoice.patient_id:
143 7c757f94 Benjamin Dauvergne
        ctx.update({
144
                'NOM_ASSURE': u' '.join((
145
                    invoice.policy_holder_first_name,
146 80f2dbfe Benjamin Dauvergne
                    invoice.policy_holder_last_name.upper())),
147 18889f61 Benjamin Dauvergne
                'NIR_ASSURE': invoice.policy_holder_social_security_id_full,
148 7c757f94 Benjamin Dauvergne
            })
149 591ca598 Benjamin Dauvergne
    total1 = Decimal(0)
150
    total2 = Decimal(0)
151 7c757f94 Benjamin Dauvergne
    tableau1 = []
152
    tableau2 = []
153 0074915c Mikaël Ates
    dates = []
154
    if invoice.list_dates:
155
        dates = invoice.list_dates.split('$')
156
    if dates:
157 15f04e07 Mikaël Ates
        if len(dates) > 30:
158 0074915c Mikaël Ates
            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 15f04e07 Mikaël Ates
        for date in dates[:15]:
167 0074915c Mikaël Ates
            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 15f04e07 Mikaël Ates
        for date in dates[15:30]:
172 0074915c Mikaël Ates
            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 7c757f94 Benjamin Dauvergne
    ctx.update({
177
            'TABLEAU1': tableau1,
178
            'TABLEAU2': tableau2,
179
        })
180
    ctx['SOUS_TOTAL1'] = total1
181 591ca598 Benjamin Dauvergne
    if total2 != Decimal(0):
182 7c757f94 Benjamin Dauvergne
        ctx['SOUS_TOTAL2'] = total2
183 278102b7 Mikaël Ates
    ctx['TOTAL'] = invoice.decimal_amount
184 c0c89c25 Serghei MIHAI
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 fa500e83 Benjamin Dauvergne
193 98f6a43c Mikaël Ates
def render_not_cmpp_header(invoicing):
194 2f2a7de9 Mikaël Ates
    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 98f6a43c Mikaël Ates
def render_not_cmpp_content(invoicing):
228 2f2a7de9 Mikaël Ates
    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 98f6a43c Mikaël Ates
        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 2f2a7de9 Mikaël Ates
        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 98f6a43c Mikaël Ates
        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 2f2a7de9 Mikaël Ates
        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 74da1939 Mikaël Ates
    list_patients.sort(key=lambda dic: dic['policy_holder'])
269 2f2a7de9 Mikaël Ates
    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 591ca598 Benjamin Dauvergne
284 dc3ed94e Benjamin Dauvergne
def render_invoicing(invoicing, delete=False, headers=True, invoices=True):
285 f3ce8efd Benjamin Dauvergne
    service = invoicing.service
286 fa500e83 Benjamin Dauvergne
    now = datetime.datetime.now()
287
    output_file = None
288 0f05168e Serghei MIHAI
    all_files = [price_details(service, invoicing)]
289 fa500e83 Benjamin Dauvergne
    try:
290 2f2a7de9 Mikaël Ates
        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 98f6a43c Mikaël Ates
            header = render_not_cmpp_header(invoicing)
304 2f2a7de9 Mikaël Ates
            all_files.append(header)
305 98f6a43c Mikaël Ates
            content = render_not_cmpp_content(invoicing)
306 2f2a7de9 Mikaël Ates
            all_files.append(content)
307 93844deb Mikaël Ates
        output_file = None
308 0d5f763c Jérôme Schneider
        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 1e5246ef Mikaël Ates
            to_path = os.path.join(settings.INVOICING_DIRECTORY, service.name)
312 26bdc73e Mikaël Ates
            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 c743d1bf Mikaël Ates
        if not output_file:
318 93844deb Mikaël Ates
            output_file = tempfile.NamedTemporaryFile(prefix='%s-invoicing-%s-' %
319
                    (service.slug, invoicing.id), suffix='-%s.pdf' % now, delete=False)
320 fa500e83 Benjamin Dauvergne
        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 7c757f94 Benjamin Dauvergne
            for path in all_files:
334 fa500e83 Benjamin Dauvergne
                try:
335
                    os.unlink(path)
336
                except:
337
                    pass
338 591ca598 Benjamin Dauvergne
339
340 dc3ed94e Benjamin Dauvergne
def batches_files(service, invoicing, health_center, batches, delete=False,
341 80f2dbfe Benjamin Dauvergne
        headers=True, invoices=True, counter=None):
342 591ca598 Benjamin Dauvergne
    files = []
343 fa500e83 Benjamin Dauvergne
    try:
344 dc3ed94e Benjamin Dauvergne
        if headers:
345 c0c89c25 Serghei MIHAI
            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 dc3ed94e Benjamin Dauvergne
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 c0c89c25 Serghei MIHAI
361 80f2dbfe Benjamin Dauvergne
                    files.extend(invoice_files(service, invoicing, batch, invoice, counter=counter))
362 7c757f94 Benjamin Dauvergne
        return files
363 fa500e83 Benjamin Dauvergne
    except:
364
        # cleanup
365
        if delete:
366 7c757f94 Benjamin Dauvergne
            for path in files:
367 fa500e83 Benjamin Dauvergne
                try:
368
                    os.unlink(path)
369
                except:
370
                    pass
371
        raise