Project

General

Profile

Download (12.8 KB) Statistics
| Branch: | Tag: | Revision:
4c578e56 Thomas NOEL
# -*- coding: utf-8 -*-

import os
import sys
5b3bf2db Thomas NOEL
import re
import glob
4c578e56 Thomas NOEL
import tempfile
5b3bf2db Thomas NOEL
import time
4c578e56 Thomas NOEL
import datetime
import hashlib
import base64
d6340428 Thomas NOEL
import json
5b3bf2db Thomas NOEL
from smtplib import SMTP, SMTPException

d6340428 Thomas NOEL
from calebasse.facturation.models import Invoicing
4c578e56 Thomas NOEL
from batches import build_batches
9527a018 Thomas NOEL
from transmission_utils import build_mail
4c578e56 Thomas NOEL
d6340428 Thomas NOEL
DEFAULT_OUTPUT_DIRECTORY = '/var/lib/calebasse/B2'
DEFAULT_NORME = 'CP '
DEFAULT_TYPE_EMETTEUR = 'TE'
DEFAULT_APPLICATION = 'TR'
DEFAULT_CATEGORIE = '189'
DEFAULT_STATUT = '60'
DEFAULT_MODE_TARIF = '05'
DEFAULT_MESSAGE = 'ENTROUVERT 0143350135 CALEBASSE 1307'

# B2 informations / configuration
# from settings.py :
# B2_TRANSMISSION = {
# 'nom': 'CMPP FOOBAR',
# 'numero_emetteur': '123456789',
# 'smtp_from': 'transmission@domaine.fr',
# ...
# }

try:
from django.conf import settings
b2_transmission_settings = settings.B2_TRANSMISSION or {}
except (ImportError, AttributeError):
b2_transmission_settings = {}
4c578e56 Thomas NOEL
5b3bf2db Thomas NOEL
# B2 informations
d6340428 Thomas NOEL
NORME = b2_transmission_settings.get('norme', DEFAULT_NORME)
4c578e56 Thomas NOEL
d6340428 Thomas NOEL
TYPE_EMETTEUR = b2_transmission_settings.get('type_emetteur', DEFAULT_TYPE_EMETTEUR)
NUMERO_EMETTEUR = b2_transmission_settings.get('numero_emetteur')
APPLICATION = b2_transmission_settings.get('application', DEFAULT_APPLICATION)
4c578e56 Thomas NOEL
d6340428 Thomas NOEL
CATEGORIE = b2_transmission_settings.get('categorie', DEFAULT_CATEGORIE)
STATUT = b2_transmission_settings.get('statut', DEFAULT_STATUT)
MODE_TARIF = b2_transmission_settings.get('mode_tarif', DEFAULT_MODE_TARIF)

NOM = b2_transmission_settings.get('nom', '')[:40]
4c578e56 Thomas NOEL
NOM = NOM + ' '*(40-len(NOM))

d6340428 Thomas NOEL
MESSAGE = b2_transmission_settings.get('message', DEFAULT_MESSAGE)[:37]
MESSAGE = MESSAGE + ' '*(37-len(MESSAGE))

# b2 output
OUTPUT_DIRECTORY = b2_transmission_settings.get('output_directory', DEFAULT_OUTPUT_DIRECTORY)
5b3bf2db Thomas NOEL
d6340428 Thomas NOEL
# mailing
SMTP_FROM = b2_transmission_settings.get('smtp_from')
SMTP_HOST = b2_transmission_settings.get('smtp_host', '127.0.0.1')
SMTP_PORT = b2_transmission_settings.get('smtp_port', 25)
SMTP_LOGIN = b2_transmission_settings.get('smtp_login')
SMTP_PASSWORD = b2_transmission_settings.get('smtp_password')
SMTP_DELAY = b2_transmission_settings.get('smtp_delay')

# if "smtp_debug_to" setting is present, send all B2 mails to this address
# instead of real ones (yy.xxx@xxx.yy.rss.fr) and output SMTP dialog
DEBUG_TO = b2_transmission_settings.get('smtp_debug_to')


def b2_is_configured():
if 'nom' in b2_transmission_settings and \
'numero_emetteur' in b2_transmission_settings and \
'smtp_from' in b2_transmission_settings:
return True
return False

def b2_output_directory():
if not os.path.isdir(OUTPUT_DIRECTORY):
raise IOError('B2 output directory (%s) is not a directory' % OUTPUT_DIRECTORY)
if not os.access(OUTPUT_DIRECTORY, os.R_OK + os.W_OK + os.X_OK):
raise IOError('B2 output directory (%s) is not accessible (rwx)' % OUTPUT_DIRECTORY)
return OUTPUT_DIRECTORY
4c578e56 Thomas NOEL
def filler(n, car=' '):
return car*n
def filler0(n):
return filler(n, '0')
def b2date(d):
return d.strftime('%y%m%d')
def get_control_key(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 '%0.2d' % (97 - (nir % 97))
except Exception, e:
return '00'


def write128(output_file, line):
if len(line) != 128:
d6340428 Thomas NOEL
raise RuntimeError('length of this B2 line is %d != 128 : "%s"' %
4c578e56 Thomas NOEL
(len(line), line))
output_file.write(line)

def write_invoice(output_file, invoice):
invoice_lines = 0
start_date = invoice.start_date
start_2 = '2' + NUMERO_EMETTEUR + ' ' + \
invoice.policy_holder_social_security_id + \
get_control_key(invoice.policy_holder_social_security_id) + \
'000' + ('%0.9d' % invoice.number) + \
'1' + ('%0.9d' % invoice.patient_id) + \
invoice.policy_holder_healthcenter.large_regime.code + \
invoice.policy_holder_healthcenter.dest_organism + \
35cfd26b Thomas NOEL
(invoice.policy_holder_other_health_center or '0000') + \
45f9a9f0 Thomas NOEL
'3' + b2date(start_date) + '000000' + \
4c578e56 Thomas NOEL
invoice.policy_holder_healthcenter.dest_organism + '000' + \
84210b62 Thomas NOEL
'10' + '3' + \
4c578e56 Thomas NOEL
b2date(start_date) + \
'000000000' + ' ' + \
b2date(invoice.patient_birthdate) + \
('%d' % invoice.patient_twinning_rank)[-1:] + \
b2date(start_date) + b2date(invoice.end_date) + '01' + \
'00' + filler(10)
write128(output_file, start_2)
invoice_lines += 1
nb_type3 = 0
2da86cbc Mikaël Ates
kind = invoice.first_tag[0]
cd591be7 Thomas NOEL
prestation = u'SNS ' if kind == 'T' else u'SD '
for date in invoice.list_dates.split('$'):
4c578e56 Thomas NOEL
line_3 = '3' + NUMERO_EMETTEUR + ' ' + \
invoice.policy_holder_social_security_id + \
get_control_key(invoice.policy_holder_social_security_id) + \
'000' + ('%0.9d' % invoice.number) + \
'19' + '320' + \
cd591be7 Thomas NOEL
b2date(datetime.datetime.strptime(date, "%d/%m/%Y")) + \
b2date(datetime.datetime.strptime(date, "%d/%m/%Y")) + \
4c578e56 Thomas NOEL
prestation + '001' + \
' ' + '00100' + ' ' + '00000' + \
('%0.7d' % invoice.ppa) + \
('%0.8d' % invoice.ppa) + \
'100' + \
('%0.8d' % invoice.ppa) + \
('%0.8d' % invoice.ppa) + \
'0000' + '000' + ' ' + filler(2) + ' ' + \
' ' + '0000000'
write128(output_file, line_3)
invoice_lines += 1
nb_type3 += 1

end_5 = '5' + NUMERO_EMETTEUR + ' ' + \
invoice.policy_holder_social_security_id + \
get_control_key(invoice.policy_holder_social_security_id) + \
'000' + ('%0.9d' % invoice.number) + \
('%0.3d' % nb_type3) + \
('%0.8d' % invoice.amount) + \
('%0.8d' % invoice.amount) + \
'00000000' + '00000000' + '00000000' + '00000000' + '00000000' + \
filler(17) + \
('%0.8d' % invoice.amount) + \
filler(4+2)
write128(output_file, end_5)
invoice_lines += 1

return invoice_lines

863f59f5 Thomas NOEL
def b2(seq_id, hc, batches):
9527a018 Thomas NOEL
to = hc.b2_000()
4c578e56 Thomas NOEL
total = sum(b.total for b in batches)
35cfd26b Thomas NOEL
first_batch = min(b.number for b in batches)
4c578e56 Thomas NOEL
d6340428 Thomas NOEL
output_dir = os.path.join(b2_output_directory(), '%s' % seq_id)
if not os.path.isdir(output_dir):
os.mkdir(output_dir)

infos = {
'seq_id': seq_id,
'hc': u'%s' % hc,
'hc_b2': to,
'batches': [],
'total': float(total)
}

4c578e56 Thomas NOEL
# B2 veut un identifiant de fichier sur 6 caractères alphanum
35cfd26b Thomas NOEL
hexdigest = hashlib.sha256('%s%s%s%s%s' % (seq_id, first_batch, NUMERO_EMETTEUR, to, total)).hexdigest()
4c578e56 Thomas NOEL
file_id = base64.encodestring(hexdigest).upper()[0:6]
2be69dbb Thomas NOEL
prefix = '%s-%s-%s-%s-%s.' % (seq_id, NUMERO_EMETTEUR, to, first_batch, file_id)
d6340428 Thomas NOEL
b2_filename = os.path.join(output_dir, prefix + 'b2')
assert not os.path.isfile(b2_filename), 'B2 file "%s" already exists' % b2_filename

4c578e56 Thomas NOEL
output_file = tempfile.NamedTemporaryFile(suffix='.b2tmp',
d6340428 Thomas NOEL
prefix=prefix, dir=output_dir, delete=False)

4c578e56 Thomas NOEL
nb_lines = 0

d6340428 Thomas NOEL
utcnow = datetime.datetime.utcnow()
4c578e56 Thomas NOEL
start_000 = '000' + TYPE_EMETTEUR + '00000' + NUMERO_EMETTEUR + \
filler(6) + to + filler(6) + APPLICATION + \
file_id + b2date(utcnow) + NORME + 'B2' + filler(15) + \
'128' + filler(6) + MESSAGE
write128(output_file, start_000)
nb_lines += 1
nb_batches = 0

for batch in batches:
start_1 = '1' + NUMERO_EMETTEUR + filler(6) + \
batch.health_center.dest_organism[0:3] + \
('%0.3d' % batch.number) + CATEGORIE + STATUT + MODE_TARIF + \
NOM + 'B2' + b2date(utcnow) + ' ' + NORME[0:2] + \
' ' + '062007' + 'U' + filler(2+3+1+34)
write128(output_file, start_1)
nb_lines += 1

d6340428 Thomas NOEL
infos['batches'].append({
'batch': batch.number,
'hc': u'%s' % batch.health_center,
'total': float(batch.total),
'number_of_invoices': batch.number_of_invoices,
'number_of_acts': batch.number_of_acts
})

4c578e56 Thomas NOEL
for i in batch.invoices:
nb_lines += write_invoice(output_file, i)

end_6 = '6' + NUMERO_EMETTEUR + \
('%0.3d' % batch.number_of_invoices) + \
('%0.4d' % batch.number_of_acts) + \
('%0.4d' % batch.number_of_invoices) + \
('%0.3d' % batch.number_of_invoices) + \
('%0.9d' % (batch.total * 100)) + \
('%0.9d' % (batch.total * 100)) + \
'000000000' + ('%0.3d' % batch.number) + \
filler(1+1+4+12+12+3+9+32)
write128(output_file, end_6)
nb_lines += 1
nb_batches += 1

863f59f5 Thomas NOEL
if nb_lines > 990:
raise

4c578e56 Thomas NOEL
end_999 = '999' + TYPE_EMETTEUR + '00000' + NUMERO_EMETTEUR + \
filler(6) + to + filler(6) + APPLICATION + \
file_id + \
('%0.8d' % (nb_lines+1)) + \
filler(19) + \
('%0.3d' % nb_batches) + \
filler(43)
write128(output_file, end_999)

old_filename = output_file.name
output_file.close()

d6340428 Thomas NOEL
b2_filename = os.path.join(output_dir, prefix + 'b2')
9527a018 Thomas NOEL
os.rename(old_filename, b2_filename)
2da86cbc Mikaël Ates
9527a018 Thomas NOEL
# create S/MIME mail
d6340428 Thomas NOEL
fd = open(b2_filename + '-mail', 'w')
fd.write(build_mail(hc.large_regime.code, hc.dest_organism, b2_filename))
fd.close()

# create info file (json)
basename = os.path.basename(b2_filename)
infos['files'] = {
'b2': basename,
'self': basename + '-info',
'mail': basename + '-mail'
}
fd = open(b2_filename + '-info', 'w')
json.dump(infos, fd, sort_keys=True, indent=4, separators=(',', ': '))
fd.close()

return b2_filename

def buildall(seq_id):
try:
invoicing = Invoicing.objects.filter(seq_id=seq_id)[0]
except IndexError:
raise RuntimeError('Facture introuvable')
batches = build_batches(invoicing)
for hc in batches:
for b in batches[hc]:
b2_filename = b2(invoicing.seq_id, hc, [b])
4c578e56 Thomas NOEL
5b3bf2db Thomas NOEL
d6340428 Thomas NOEL
def sendmail_raw(mail):
5b3bf2db Thomas NOEL
if DEBUG_TO:
toaddr = DEBUG_TO
print '(debug mode, sending to', toaddr, ')'
else:
toaddr = re.search('\nTo: +(.*)\n', mail, re.MULTILINE).group(1)

d6340428 Thomas NOEL
smtp = SMTP(SMTP_HOST, SMTP_PORT)
5b3bf2db Thomas NOEL
if DEBUG_TO:
smtp.set_debuglevel(1)
d6340428 Thomas NOEL
smtp.ehlo()
if SMTP_LOGIN and SMTP_PASSWORD:
smtp.starttls()
smtp.ehlo()
5b3bf2db Thomas NOEL
smtp.login(SMTP_LOGIN, SMTP_PASSWORD)
d6340428 Thomas NOEL
smtp.sendmail(SMTP_FROM, toaddr, mail)
5b3bf2db Thomas NOEL
smtp.close()
d6340428 Thomas NOEL
return toaddr, "%s:%s" % (SMTP_HOST, SMTP_PORT)

def sendmail(seq_id, oneb2=None):
output_dir = os.path.join(b2_output_directory(), '%s' % seq_id)
if oneb2:
filename = os.path.join(output_dir, oneb2 + '-mail')
if os.path.isfile(filename + '-sent'): # resent
os.rename(filename + '-sent', filename)
filenames = [filename]
else:
filenames = glob.glob(os.path.join(output_dir, '*.b2-mail'))
for mail_filename in filenames:
log = open(mail_filename + '.log', 'a')
log.write('%s mail %s\n' % (datetime.datetime.now(), os.path.basename(mail_filename)))
5b3bf2db Thomas NOEL
mail = open(mail_filename).read()
try:
d6340428 Thomas NOEL
to, via = sendmail_raw(mail)
5b3bf2db Thomas NOEL
except SMTPException as e:
d6340428 Thomas NOEL
log.write('%s SMTP ERROR: %s\n' % (datetime.datetime.now(), e))
5b3bf2db Thomas NOEL
else:
d6340428 Thomas NOEL
log.write('%s OK, MAIL SENT TO %s VIA %s\n' % (datetime.datetime.now(), to, via))
5b3bf2db Thomas NOEL
os.rename(mail_filename, mail_filename + '-sent')
d6340428 Thomas NOEL
os.utime(mail_filename + '-sent', None) # touch
log.close()
if SMTP_DELAY:
time.sleep(SMTP_DELAY) # Exchange, I love you.


def get_all_infos(seq_id):
output_dir = os.path.join(b2_output_directory(), '%s' % seq_id)
infos = []
for mail_filename in glob.glob(os.path.join(output_dir, '*.b2-info')):
fd = open(mail_filename, 'r')
info = json.load(fd)
stats = os.stat(os.path.join(output_dir, info['files']['b2']))
info['creation_date'] = datetime.datetime.fromtimestamp(stats.st_mtime)
try:
stats = os.stat(os.path.join(output_dir, info['files']['mail'] + '-sent'))
info['mail_date'] = datetime.datetime.fromtimestamp(stats.st_mtime)
except:
pass
try:
fd = open(os.path.join(output_dir, info['files']['mail'] + '.log'), 'r')
info['mail_log'] = fd.read()
fd.close()
except:
pass
infos.append(info)
fd.close()
return infos