Project

General

Profile

« Previous | Next » 

Revision 84fd22a0

Added by Benjamin Dauvergne about 12 years ago

[domino] finish family update workflow

- when creating or updating a family, store the family.code_interne value into
the user object for later retrieval,
- add web service API to retrieve person to contact for children

View differences:

extra/modules/abelium_domino_synchro.py
21 21
    client.clear_cache()
22 22
    users = User.values()
23 23
    users_by_mail = dict(((user.email, user) for user in users))
24
    users_by_code_interne = {}
25
    for user in users:
26
        if hasattr(user, 'abelium_domino_code_famille'):
27
            users_by_code_interne[user.abelium_domino_code_famille] = user
24 28
    try:
25
        invoices = client.get_invoices(state='TOUTES')
29
        invoices = client.invoices
26 30
    except abelium_domino_ws.DominoException, e:
27
        logger.error('failure to retrieve invoice list from domino '
31
        logger.debug('domino cron: failure to retrieve invoice list from domino '
28 32
                'for synchronization [error:%s]', e)
29 33
        return
30 34
    # import new invoices
31
    for invoice in invoices:
35
    logger.debug('domino cron: retrieved %i invoices', len(invoices))
36
    for invoice_id, invoice in invoices.iteritems():
32 37
        user = None
33
        for email in (invoice.family.email_pere, invoice.family.email_mere,
34
                invoice.family.adresse_internet):
35
            user = users_by_mail.get(email)
36
            if user:
37
                break
38
        else:
39
            continue
38
        if invoice.family.code_interne in users_by_code_interne:
39
            user = users_by_code_interne[invoice.family.code_interne]
40
        if user is None:
41
            for email in (invoice.family.email_pere, invoice.family.email_mere,
42
                    invoice.family.adresse_internet):
43
                user = users_by_mail.get(email)
44
                if user:
45
                    break
46
            else:
47
                continue
40 48
        external_id = '%s%s' % (DOMINO_ID_PREFIX, invoice.id)
41
        payment_invoice = Invoice.get_on_index(external_id, 'external_id',)
49
        payment_invoice = Invoice.get_on_index(external_id, 'external_id', ignore_errors=True)
42 50
        if payment_invoice:
43 51
            continue
44 52
        payment_invoice = Invoice()
......
51 59
        payment_invoice.date = invoice.creation
52 60
        payment_invoice.domino_synchro_date = datetime.now()
53 61
        payment_invoice.store()
54
        logger.info('remote invoice %s for family %s added to user %s invoices with id %s',
62
        logger.info('domino cron: remote invoice %s for family %s added to user %s invoices with id %s',
55 63
                invoice.id, invoice.family.id, user.id, payment_invoice.id)
56 64

  
57 65
    # update invoices
58
    invoices_ids = dict(((invoice.id, invoice) for invoice in invoices))
66
    invoices_ids = dict(invoices.iteritems())
59 67
    for payment_invoice in Invoice.values():
60
        if not payment_invoice.external_id.starswith(DOMINO_ID_PREFIX):
68
        if payment_invoice.external_id is None or not payment_invoice.external_id.startswith(DOMINO_ID_PREFIX):
61 69
            continue # not a payment related to domino we skip
62 70
        i = payment_invoice.external_id[len(DOMINO_ID_PREFIX):]
63 71
        i = int(i)
......
67 75
                # invoice has been paid (locally or not) but remote invoice has
68 76
                # been deleted do, we do nothing.
69 77
                continue
70
            if getattr(payment_invoice, 'domino_knows_its_paid'):
78
            if getattr(payment_invoice, 'domino_knows_its_paid', None) or getattr(payment_invoice, 'paid_by_domino', None):
71 79
                # synchronization of payment already done, skip
72 80
                continue
73 81
            transactions = Transaction.get_with_indexed_value('invoice_ids', payment_invoice.id)
74 82
            if not transactions:
75
                logger.warning("invoice %s is marked paid but does "
83
                logger.warning("domino cron: invoice %s is marked paid but does "
76 84
                        "not have any linked transaction.", payment_invoice.id)
77 85
                details = '' # no details about the payment, problem
78 86
            else:
......
83 91
                client.pay_invoice([invoice], invoice.montant, details,
84 92
                        payment_invoice.paid_date)
85 93
            except abelium_domino_ws.DominoException, e:
86
                logger.error('invoice %s has been paid, but the remote system '
94
                logger.error('domino cron: invoice %s has been paid, but the remote system '
87 95
                        'is unreachable, notification will be done again '
88 96
                        'later [error: %s]', invoice.id, e)
89 97
            else:
90 98
                # memorize the date of synchronization
91 99
                payment_invoice.domino_knows_its_paid = datetime.now()
92 100
                payment_invoice.store()
93
                logger.info('invoice %s has been paid; remote system has been '
101
                logger.info('domino cron: domino: invoice %s has been paid; remote system has been '
94 102
                        'notified', payment_invoice.id)
95 103
        else: # unpaid
96 104
            if not invoice:
97
                logger.info('remote invoice %s disapearred, so its '
105
                logger.info('domino cron: remote invoice %s disapearred, so its '
98 106
                        'still-unpaid local counterpart invoice %s was deleted.',
99 107
                        i, payment_invoice.id)
100 108
                payment_invoice.remove_self()
......
104 112
                payment_invoice.paid_date = datetime.now()
105 113
                payment_invoice.paid_by_domino = True
106 114
                payment_invoice.store()
107
                logging.info('remote invoice %s has beend paid, '
115
                logger.info('domino cron: remote invoice %s has beend paid, '
108 116
                        'local invoice %s of user %s is now marked as paid.',
109 117
                        invoice.id, payment_invoice.id, payment_invoice.user_id)
110 118
            else: # not invoice.paid()
111 119
                pass # still waiting for the payment
112 120

  
113 121
get_publisher_class().register_cronjob(CronJob(function=synchronize_domino,
114
    hours=range(24), minutes=range(0,60,10)))
122
    hours=range(0, 24), minutes=range(0, 60)))
extra/modules/abelium_domino_ui.ptl
1
from quixote import get_publisher, redirect
1
from quixote import get_publisher, redirect, get_request
2 2
from quixote.directory import Directory, AccessControlled
3 3

  
4 4
from qommon import get_cfg
......
7 7

  
8 8
from payments import Regie
9 9

  
10

  
10 11
# constants
11 12
ABELIUM_DOMINO = 'abelium_domino'
12 13
ACTIVATED = 'activated'
......
32 33
    cfg = get_abelium_cfg(publisher)
33 34
    return cfg.get(ACTIVATED, False) and abelium_domino_ws is not None
34 35

  
35
_WS_CACHE = None
36

  
37 36
def get_client(publisher=None):
38
    global _WS_CACHE
37
    publisher = publisher or get_publisher()
39 38

  
40 39
    cfg = get_abelium_cfg(publisher)
41
    if _WS_CACHE is None:
42
        _WS_CACHE = abelium_domino_ws.DominoWs(
43
            url=cfg.get(WSDL_URL, ''),
44
            domain=cfg.get(DOMAIN,''),
45
            login=cfg.get(LOGIN, ''),
46
            password=cfg.get(PASSWORD, ''),
47
            location=cfg.get(SERVICE_URL))
48
    return _WS_CACHE
40
    publisher._ws_cache = abelium_domino_ws.DominoWs(
41
        url=cfg.get(WSDL_URL, ''),
42
        domain=cfg.get(DOMAIN,''),
43
        login=cfg.get(LOGIN, ''),
44
        password=cfg.get(PASSWORD, ''),
45
        location=cfg.get(SERVICE_URL))
46
    return publisher._ws_cache
47

  
48
def get_family(user, publisher=None):
49
    family = None
50
    if user is None:
51
        return None
52
    client = get_client(publisher)
53
    if hasattr(user, 'abelium_domino_code_famille'):
54
        family = client.get_family_by_code_interne(
55
            user.abelium_domino_code_famille)
56
    if family is None and user.email:
57
        family = client.get_family_by_mail(user.email)
58
    return family
49 59

  
50 60
def get_invoice_regie(publisher=None):
51 61
    cfg = get_abelium_cfg(publisher)
......
59 69
    label = N_('Domino')
60 70

  
61 71
    def debug [html] (self):
72
        from abelium_domino_vars import SESSION_CACHE
62 73
        html_top(ABELIUM_DOMINO)
74
        '<p>code interne: %s</p>' % getattr(get_request().user, str('abelium_domino_code_famille'), None)
63 75
        '<dl>'
64 76
        context = get_publisher().substitutions.get_context_variables()
65 77
        for var in sorted(context.keys()):
66 78
            value = context[var]
67 79
            if value:
68 80
                '<dt>%s</dt>' % var
69
                '<dd>%s</dt>' % unicode(value).encode(get_publisher().site_charset)
81
                '<dd>%s</dt>' % value
70 82
        '</dl>'
71

  
83
        delattr(get_request().session, SESSION_CACHE)
72 84

  
73 85
    def _q_index [html] (self):
74 86
        publisher = get_publisher()
......
98 110
                    'activated because of this error when '
99 111
                    'loading it: %r') % import_error
100 112
            '<p class="errornotice">%s</p>' % message
113
        '<dl>'
114
        context = get_publisher().substitutions.get_context_variables()
115
        for var in sorted(context.keys()):
116
            value = context[var]
117
            if value:
118
                '<dt>%s</dt>' % var
119
                '<dd>%s</dt>' % unicode(value).encode(get_publisher().site_charset)
120
        '</dl>'
101 121

  
102 122
    form_desc = (
103 123
            # name, required, title, kind
......
129 149
        form.add_submit('cancel', _('Cancel'))
130 150

  
131 151
        return form
132

  
extra/modules/abelium_domino_vars.py
1 1
from decimal import Decimal
2 2
import logging
3 3

  
4
from quixote.publish import get_publisher
5

  
4 6
from qommon.substitution import Substitutions
5 7
from publisher import WcsPublisher
6 8

  
7
from abelium_domino_ui import (is_activated, abelium_domino_ws, get_client)
9
from abelium_domino_ui import (is_activated, abelium_domino_ws, get_client, get_family)
8 10

  
9 11
SESSION_CACHE = 'abelium_domino_variable_cache'
10 12

  
......
31 33
            return cache
32 34
        # call the web service
33 35
        try:
34
            client = get_client()
35
            family = client.get_family_by_mail(self.request.user.email)
36
            charset = get_publisher().site_charset
37
            family = get_family(self.request.user)
36 38
            if family:
37 39
                family.complete()
38 40
                for i, child in enumerate(family.children):
......
40 42
                        v = getattr(child, name, None)
41 43
                        if v is None:
42 44
                            continue
45
                        if hasattr(v, 'encode'):
46
                            v = v.encode(charset)
43 47
                        vars[self.CHILD_VARIABLE_TEMPLATE % (name, i+1)] = v
48
                vars[self.VARIABLE_TEMPLATE % 'nombre_enfants'] = len(family.children)
44 49
                for remote_name, name, converted, desc in self.FAMILY_COLUMNS:
45 50
                    if hasattr(family, name):
46 51
                        v = getattr(family, name)
52
                        if v is None:
53
                            continue
54
                        if hasattr(v, 'encode'):
55
                            v = v.encode(charset)
47 56
                        vars[self.VARIABLE_TEMPLATE % name] = v
48 57
                amount = Decimal(0)
49 58
                for invoice in family.invoices:
......
52 61
                    vars['user_famille_reste_du'] = str(amount)
53 62
        except abelium_domino_ws.DominoException:
54 63
            logging.exception('unable to call the domino ws for user %s', self.request.user.id)
55
	    setattr(self.request.session, SESSION_CACHE, vars)
56
	    self.request.session.store()
64
        setattr(self.request.session, SESSION_CACHE, vars)
65
        self.request.session.store()
57 66
        return vars
58 67

  
59 68
    def get_substitution_variables_list(cls):
60 69
        if not is_activated():
61 70
            return ()
62 71
        vars = []
63
        for remote_name, name, converted, desc in self.FAMILY_COLUMNS:
72
        for remote_name, name, converted, desc in cls.FAMILY_COLUMNS:
64 73
            vars.append((_('Domino'), cls.VARIABLE_TEMPLATE % name, desc))
65
        for remote_name, name, converted, desc in self.CHILD_COLUMNS:
74
        for remote_name, name, converted, desc in cls.CHILD_COLUMNS:
66 75
            vars.append((_('Domino'), cls.CHILD_VARIABLE_TEMPLATE % (name, '{0,1,2,..}'), desc))
67 76
        return vars
68 77
    get_substitution_variables_list = classmethod(get_substitution_variables_list)
extra/modules/abelium_domino_workflow.py
1
from quixote import get_request
1
import re
2
import time
3

  
4
from quixote import get_request, get_publisher, get_session
5
from quixote.directory import Directory
2 6

  
3 7
from qommon.substitution import Substitutions
8
from qommon.form import Form, StringWidget
9
import qommon.misc
10
from qommon import get_logger
4 11
from publisher import WcsPublisher
5 12

  
6
from wcs.workflows import Workflow, WorkflowStatusItem, register_item_class, \
13
from wcs.workflows import Workflow, WorkflowStatusJumpItem, register_item_class, \
7 14
        render_list_of_roles, get_role_translation
15
from wcs.forms.common import FormStatusPage
8 16

  
17
from abelium_domino_ui import (is_activated, abelium_domino_ws, get_client, get_family)
18
import abelium_domino_ws
9 19

  
10

  
11
class AbeliumDominoRegisterFamilyWorkflowStatusItem(WorkflowStatusItem):
20
class AbeliumDominoRegisterFamilyWorkflowStatusItem(WorkflowStatusJumpItem):
21
    status = None
12 22
    description = N_('Abelium Domino: Register a Family')
13 23
    key = 'abelium-domino-register-family'
14
    endpoint = False
24
    label = None
15 25

  
16 26
    def render_as_line(self):
17 27
        return _('Register a Family into Abelium Domino')
18 28

  
19
    def get_parameters(self):
20
        return ()
29
    def get_family(self, formdata):
30
        try:
31
            user = formdata.get_user()
32
            if user:
33
                family = get_family(user)
34
                if family:
35
                    family.complete()
36
                return family
37
        except abelium_domino_ws.DominoException:
38
            pass
39
        return None
40

  
41
    def fill_form(self, form, formdata, user):
42
        family = self.get_family(formdata)
43
        if 'family_id' not in form._names:
44
            form.add(StringWidget, 'family_id', title=_('Family internal code'),
45
                     value=family and family.code_interne.encode('utf8'))
46
            if not family:
47
                form.add_submit('create_button%s' % self.id, _('Create the family'))
48
            form.add_submit('update_button%s' % self.id, _('Update the family'))
49

  
50
    def update(self, form, formdata, user, evo):
51
        fid_widget = form.get_widget('family_id')
52
        code_interne = fid_widget.parse()
53
        try:
54
            code_interne = int(code_interne)
55
        except ValueError:
56
            raise ValueError('Le code interne est invalide')
57
        code_interne = '%05d' % code_interne
58
        family = get_client().get_family_by_code_interne(code_interne)
59
        if not family:
60
            raise ValueError('Le code interne est invalide')
61
        family.complete()
62
        self.extract_family(form, formdata, user, evo, family)
63
        family.save()
64
        return family
21 65

  
22
    def add_parameters_widgets(self, form, parameters, prefix='', formdef=None):
23
        pass
66
    def create(self, form, formdata, user, evo):
67
        family = abelium_domino_ws.Family(client=get_client())
68
        self.extract_family(form, formdata, user, evo, family)
69
        return family
24 70

  
25
    def perform(self, formdata):
26
        family_data = {}
27
        for k, v in formdata.get_as_dict().items():
28
            if k.startswith('var_'):
29
                family_data['domino_'+k] = v
30
        # XXX: store family data locally, this should be changed to send it
31
        # over to the abelium domino web service.
32
        get_request().user.family_data = family_data
33
        get_request().user.store()
71
    def extract_family(self, form, formdata, user, evo, family):
72
        formdef = formdata.formdef
73
        children = [abelium_domino_ws.Child() for i in range(5)]
74
        max_i = 0
75
        for field in formdef.fields:
76
            value = formdata.data.get(field.id)
77
            if value in (None, ''):
78
                continue
79
            if hasattr(field, 'date_in_the_past'):
80
                value = time.strftime('%Y%m%d', value)
81
            value = unicode(value, 'utf8')
82
            if field.prefill and \
83
               field.prefill.get('type') == 'formula':
84
                v = field.prefill.get('value', '').strip()
85
                i = None
86
                name = None
87
                m = re.search('domino_var_([^ ]*)_enfant([0-9]*)', v)
88
                m2 = re.search('domino_var_([^ ]*)', v)
89
                if m:
90
                    name, i = m.groups()
91
                    try:
92
                        i = int(i)
93
                    except ValueError:
94
                        continue
95
                    max_i = max(i, max_i)
96
                    print 'enfant', name, i-1, value
97
                    setattr(children[i-1], name, value)
98
                elif m2:
99
                    name = m2.group(1)
100
                    print 'family', name, value
101
                    setattr(family, name, value)
102
        for child1, child2 in zip(family.children, children):
103
            child1.__dict__.update(child2.__dict__)
104
        family.save()
105
        if max_i > len(family.children): # add new children
106
            for child in children[len(family.children):max_i]:
107
                family.add_child(child)
34 108

  
109
    def submit_form(self, form, formdata, user, evo):
110
        logger = get_logger()
111
        try:
112
            if form.get_submit() == 'update_button%s' % self.id:
113
                family = self.update(form, formdata, user, evo)
114
                msg = _('Sucessfully updated the family %s')
115
                log_msg = _('Sucessfully updated the family %s of %s')
116
            elif form.get_submit() == 'create_button%s' % self.id:
117
                family = self.create(form, formdata, user, evo)
118
                msg = _('Sucessfully created the family %s')
119
                log_msg = _('Sucessfully created the family %s of %s')
120
            else:
121
                raise NotImplemented
122
            code_interne = family.code_interne.encode('utf8')
123
            msg = msg % code_interne
124
            logger.info(_('Sucessfully created the family %s of %s'),
125
                    code_interne, formdata.get_user())
126
            form_user = formdata.get_user()
127
            form_user.abelium_domino_code_famille = code_interne
128
            form_user.store()
129
        except Exception, e:
130
            raise
131
            if form.get_submit() == 'update_button%s' % self.id:
132
                msg = _('Unable to update family: %s') % str(e)
133
            elif form.get_submit() == 'create_button%s' % self.id:
134
                msg = _('Unable to create family: %s') % str(e)
135
            evo.comment = msg
136
            logger.exception(msg  % formdata.get_user())
137
        else:
138
            evo.comment = msg
139
            wf_status = self.get_status()
140
            if wf_status:
141
                evo.status = 'wf-%s' % wf_status.id
142
        return False
35 143

  
36 144
register_item_class(AbeliumDominoRegisterFamilyWorkflowStatusItem)
extra/modules/abelium_domino_ws.py
1 1
# -*- coding: utf-8 -*-
2 2
from decimal import Decimal
3
import time
3 4
import datetime
4 5
from xml.etree import ElementTree as etree
5 6
import logging
......
20 21
    return unicode(x).strip()
21 22

  
22 23
def strip_and_int(x):
23
    return int(x.strip())
24
    try:
25
        return int(x.strip())
26
    except ValueError:
27
        return None
28

  
29
def strip_and_date(x):
30
    try:
31
        return datetime.datetime.strptime(x.strip(), '%Y%m%d').date()
32
    except ValueError:
33
        return None
24 34

  
25 35
def parse_date(date_string):
26 36
    if date_string:
......
38 48
    def decorated_function(self, *args, **kwargs):
39 49
        cache_name = '__%s_cache' % function.__name__
40 50
        if not hasattr(self, cache_name):
41
            setattr(self, cache_name, {})
42
        d = getattr(self, cache_name)
51
            setattr(self, cache_name, (time.time(), {}))
52
        t, d = getattr(self, cache_name)
53
        if time.time() - t > 30:
54
            setattr(self, cache_name, (time.time(), {}))
55
            t, d = getattr(self, cache_name)
43 56
        k = tuple(*args) + tuple(sorted(kwargs.items()))
44 57
        if not k in d:
45 58
            d[k] = function(self, *args, **kwargs)
......
73 86
            v = getattr(self, local_name, None)
74 87
            if v is None:
75 88
                continue
76
            v = unicode(v).encode('utf-8')
77
            l.append('{0}: "{1}"'.format(remote_name, v))
78
        return ','.join(l)
89
            l.append(u'{0}: "{1}"'.format(remote_name, v))
90
        return u','.join(l)
79 91

  
80 92
    def debug(self):
81 93
        '''Output a debugging view of this object'''
94
        res = ''
82 95
        for remote_name, name, converter, desc in self.MORE_COLUMNS or self.COLUMNS:
83 96
            if hasattr(self, name):
84
                print name, ':', getattr(self, name)
97
                res += name + ':' + repr(getattr(self, name)) + '\n'
98
        return res
85 99

  
86 100
    def __int__(self):
87 101
        '''Return the object id'''
88 102
        return self.id
89 103

  
104
class UrgentContact(SimpleObject):
105
    COLUMNS = (
106
            ('IDENFANTS', 'id_enfant', strip_and_int, 'IDENFANTS'),
107
            ('IDCONTACT_AUTORISE', 'id', strip_and_int, 'IDCONTACT_AUTORISE'),
108
            ('LIENFAMILLE_CH', 'lien_de_famille', unicode_and_strip, 'LIENFAMILLE_CH'),
109
            ('PERE_MERE_CH', 'lien_pere_ou_pere', unicode_and_strip, 'PERE_MERE_CH'),
110
            ('IDFAMILLES', 'id_famille', unicode_and_strip, 'IDFAMILLES'),
111
            ('TYPE_CH', 'type', unicode_and_strip, 'TYPE_CH'),
112
            ('NOM_CH', 'nom', unicode_and_strip, 'NOM_CH'),
113
            ('PRENOM_CH', 'prenom', unicode_and_strip, 'PRENOM_CH'),
114
            ('RUE_CH', 'rue', unicode_and_strip, 'RUE_CH'),
115
            ('RUE2_CH', 'rue2', unicode_and_strip, 'RUE2_CH'),
116
            ('RUE3_CH', 'rue3', unicode_and_strip, 'RUE3_CH'),
117
            ('CODEPOSTAL_CH', 'code_postal', unicode_and_strip, 'CODEPOSTAL_CH'),
118
            ('VILLE_CH', 'ville', unicode_and_strip, 'VILLE_CH'),
119
            ('TELEPHONE_CH', 'telephone', unicode_and_strip, 'TELEPHONE_CH'),
120
            ('TELEPHONE2_CH', 'telephone2', unicode_and_strip, 'TELEPHONE2_CH'),
121
            ('ADRESSEINT_CH', 'adresse_internet', unicode_and_strip, 'ADRESSEINT_CH'),
122
    )
123

  
90 124
class Child(SimpleObject):
91 125
    COLUMNS = (
92 126
            ('IDENFANTS', 'id', strip_and_int, 'Identifiant de ENFANTS'),
93
            ('NOM_CH', 'nom',unicode_and_strip, 'Nom'),
94
            ('PRENOM_CH', 'prenom',unicode_and_strip, 'Prénom'),
95
            ('NAISSANCE_DA', 'naissance',unicode_and_strip, 'Date de Naissance'),
96
            ('COMMENTAIRE_ME', 'commentaire',unicode_and_strip, 'Commentaires / Notes'),
97
            ('IDFAMILLES', 'id_famille',unicode_and_strip, 'IDFAMILLES'),
98
            ('CODEPOSTAL_CH', 'code_postal',unicode_and_strip, 'Code Postal'),
99
            ('VILLE_CH', 'ville',unicode_and_strip, 'Ville'),
100
            ('CODEINTERNE_CH', 'code_interne',unicode_and_strip, 'Code Interne'),
101
            ('LIEUNAISSANCE_CH', 'lieu_naissance',unicode_and_strip, 'Lieu de Naissance'),
102
            ('DEPNAISSANCE_CH', 'departement_naissance',unicode_and_strip, 'Département Naissance'),
103
            ('NUMSECU_CH', 'num_securite_sociale',unicode_and_strip, 'N° de SECU'),
104
            ('NATIONALITE_CH', 'nationalite',unicode_and_strip, 'Nationalité'),
105
            ('PRENOM2_CH', 'prenom2',unicode_and_strip, 'Prénom 2'),
106
            ('SEXE_CH', 'sexe',unicode_and_strip, 'Sexe'),
107
            ('IDTABLELIBRE1', 'IDTABLELIBRE1',unicode_and_strip, 'IDTABLELIBRE1'),
108
            ('IDTABLELIBRE2', 'IDTABLELIBRE2',unicode_and_strip, 'IDTABLELIBRE2'),
109
            ('IDTABLELIBRE3', 'IDTABLELIBRE3',unicode_and_strip, 'IDTABLELIBRE3'),
110
            ('IDTABLELIBRE4', 'IDTABLELIBRE4',unicode_and_strip, 'IDTABLELIBRE4'),
111
            ('CHAMPLIBRE1_CH', 'CHAMPLIBRE1_CH',unicode_and_strip, 'Valeur Champ Libre 1'),
112
            ('CHAMPLIBRE2_CH', 'CHAMPLIBRE2_CH',unicode_and_strip, 'Valeur Champ Libre 2'),
113
            ('CHAMPCALCULE1_CH', 'CHAMPCALCULE1_CH',unicode_and_strip, 'Valeur Champ Calculé 1'),
114
            ('CHAMPCALCULE2_CH', 'CHAMPCALCULE2_CH',unicode_and_strip, 'Valeur Champ Calculé 2'),
115
            ('SOMMEIL_ME', 'sommeil',unicode_and_strip, 'Sommeil'),
116
            ('ACTIVITE_ME', 'activite',unicode_and_strip, 'Activités'),
117
            ('HABITUDE_ME', 'habitude',unicode_and_strip, 'Habitudes'),
118
            ('PHOTO_CH', 'photographie',unicode_and_strip, 'Photographie'),
119
            ('NUMCOMPTE_CH', 'numcompte',unicode_and_strip, 'N° Compte Comptable'),
120
            ('TELEPHONE_CH', 'telephone',unicode_and_strip, 'Téléphone'),
121
            ('IDFAMILLES2', 'id_famille2',unicode_and_strip, 'Identifiant famille 2'),
122
            ('PERE_CH', 'pere',unicode_and_strip, 'Nom du père'),
123
            ('MERE_CH', 'mere',unicode_and_strip, 'Nom de la mère'),
124
            ('AUTOPARENTALEMERE_IN', 'autorisation_parentale_mere',unicode_and_strip, 'Autorisation Parentale Mère'),
125
            ('AUTOPARENTALEPERE_IN', 'autorisation_parentale_pere',unicode_and_strip, 'Autorisation Parentale de Père'),
126
            ('IDPORTAIL_ENFANTS', 'id_portail_enfants',unicode_and_strip, 'Identifiant de PORTAIL_ENFANTS'),
127
            ('ADRESSEINT_CH', 'adresse_internet',unicode_and_strip, 'Adresse Internet'),
127
            ('NOM_CH', 'nom', unicode_and_strip, 'Nom'),
128
            ('PRENOM_CH', 'prenom', unicode_and_strip, 'Prénom'),
129
            ('NAISSANCE_DA', 'date_naissance', strip_and_date, 'Date de Naissance'),
130
            ('COMMENTAIRE_ME', 'commentaire', unicode_and_strip, 'Commentaires / Notes'),
131
            ('IDFAMILLES', 'id_famille', unicode_and_strip, 'IDFAMILLES'),
132
            ('CODEPOSTAL_CH', 'code_postal', unicode_and_strip, 'Code Postal'),
133
            ('VILLE_CH', 'ville', unicode_and_strip, 'Ville'),
134
            ('CODEINTERNE_CH', 'code_interne', unicode_and_strip, 'Code Interne'),
135
            ('LIEUNAISSANCE_CH', 'lieu_naissance', unicode_and_strip, 'Lieu de Naissance'),
136
            ('DEPNAISSANCE_CH', 'departement_naissance', unicode_and_strip, 'Département Naissance'),
137
            ('NUMSECU_CH', 'num_securite_sociale', unicode_and_strip, 'N° de SECU'),
138
            ('NATIONALITE_CH', 'nationalite', unicode_and_strip, 'Nationalité'),
139
            ('PRENOM2_CH', 'prenom2', unicode_and_strip, 'Prénom 2'),
140
            ('SEXE_CH', 'sexe', unicode_and_strip, 'Sexe'),
141
            ('IDTABLELIBRE1', 'IDTABLELIBRE1', unicode_and_strip, 'IDTABLELIBRE1'),
142
            ('IDTABLELIBRE2', 'IDTABLELIBRE2', unicode_and_strip, 'IDTABLELIBRE2'),
143
            ('IDTABLELIBRE3', 'IDTABLELIBRE3', unicode_and_strip, 'IDTABLELIBRE3'),
144
            ('IDTABLELIBRE4', 'IDTABLELIBRE4', unicode_and_strip, 'IDTABLELIBRE4'),
145
            ('CHAMPLIBRE1_CH', 'CHAMPLIBRE1_CH', unicode_and_strip, 'Valeur Champ Libre 1'),
146
            ('CHAMPLIBRE2_CH', 'CHAMPLIBRE2_CH', unicode_and_strip, 'Valeur Champ Libre 2'),
147
            ('CHAMPCALCULE1_CH', 'CHAMPCALCULE1_CH', unicode_and_strip, 'Valeur Champ Calculé 1'),
148
            ('CHAMPCALCULE2_CH', 'CHAMPCALCULE2_CH', unicode_and_strip, 'Valeur Champ Calculé 2'),
149
            ('SOMMEIL_ME', 'sommeil', unicode_and_strip, 'Sommeil'),
150
            ('ACTIVITE_ME', 'activite', unicode_and_strip, 'Activités'),
151
            ('HABITUDE_ME', 'habitude', unicode_and_strip, 'Habitudes'),
152
            ('PHOTO_CH', 'photographie', unicode_and_strip, 'Photographie'),
153
            ('NUMCOMPTE_CH', 'numcompte', unicode_and_strip, 'N° Compte Comptable'),
154
            ('TELEPHONE_CH', 'telephone', unicode_and_strip, 'Téléphone'),
155
            ('IDFAMILLES2', 'id_famille2', unicode_and_strip, 'Identifiant famille 2'),
156
            ('PERE_CH', 'pere', unicode_and_strip, 'Nom du père'),
157
            ('MERE_CH', 'mere', unicode_and_strip, 'Nom de la mère'),
158
            ('AUTOPARENTALEMERE_IN', 'autorisation_parentale_mere', unicode_and_strip, 'Autorisation Parentale Mère'),
159
            ('AUTOPARENTALEPERE_IN', 'autorisation_parentale_pere', unicode_and_strip, 'Autorisation Parentale de Père'),
160
            ('IDPORTAIL_ENFANTS', 'id_portail_enfants', unicode_and_strip, 'Identifiant de PORTAIL_ENFANTS'),
161
            ('ADRESSEINT_CH', 'adresse_internet', unicode_and_strip, 'Adresse Internet'),
128 162
    )
129 163

  
130 164
    def save(self):
......
132 166
            self.client.update_child(self)
133 167
        else:
134 168
            self.id = self.client.add_child(self)
169
        self.client.clear_cache()
135 170

  
136 171
class Family(SimpleObject):
137 172
    COLUMNS = (
......
140 175
            ('EMAILPERE_CH', 'email_pere', unicode_and_strip, 'email du père'),
141 176
            ('EMAILMERE_CH', 'email_mere', unicode_and_strip, 'email de la mère'),
142 177
            ('ADRESSEINT_CH', 'adresse_internet', unicode_and_strip, 'adresse internet'),
178
            ('CODEINTERNE_CH', 'code_interne', unicode_and_strip, 'code interne'),
143 179
    )
144 180

  
145 181
    MORE_COLUMNS = (
......
158 194
            ('TELECOPIE2_CH', 'telecopie2', unicode_and_strip, 'télécopie 2'),
159 195
            ('ADRESSEINT_CH', 'adresse_internet', unicode_and_strip, 'adresse internet'),
160 196
            ('SITUATION_CH', 'situation', unicode_and_strip, 'situation familiale'),
161
            ('REVENUMENSUEL_MO', 'revenu_mensuel_mo', unicode_and_strip, 'revenu mensuel de la famille'),
162
            ('REVENUANNUEL_MO', 'revenu_annuel_mo', unicode_and_strip, 'revenu annuel de la famille'),
163
            ('QUOTIENTFAMILIAL_MO', 'quotient_familial_mo', unicode_and_strip, 'quotient familial'),
164
            ('NBTOTALENFANTS_EN', 'nb_total_enfants_en', unicode_and_strip, 'nombre total d\'enfants'),
165
            ('NBENFANTSACHARGE_EN', 'nb_enfants_a_charge_en', unicode_and_strip, 'nombre d\'enfants à charge'),
197
            ('REVENUMENSUEL_MO', 'revenu_mensuel', unicode_and_strip, 'revenu mensuel de la famille'),
198
            ('REVENUANNUEL_MO', 'revenu_annuel', unicode_and_strip, 'revenu annuel de la famille'),
199
            ('QUOTIENTFAMILIAL_MO', 'quotient_familial', unicode_and_strip, 'quotient familial'),
200
            ('NBTOTALENFANTS_EN', 'nb_total_enfants', unicode_and_strip, 'nombre total d\'enfants'),
201
            ('NBENFANTSACHARGE_EN', 'nb_enfants_a_charge', unicode_and_strip, 'nombre d\'enfants à charge'),
166 202
            ('NOMPERE_CH', 'nom_pere', unicode_and_strip, 'monsieur'),
167 203
            ('PRENOMPERE_CH', 'prenom_pere', unicode_and_strip, 'prénom monsieur'),
168
            ('AUTOPARENTALEPERE_IN', 'autoparentale_pere_in', unicode_and_strip, 'autorisation parentale de père'),
169
            ('DATENAISPERE_DA', 'date_naissance_pere', unicode_and_strip, 'date de naisance du père'),
204
            ('AUTOPARENTALEPERE_IN', 'autoparentale_pere', unicode_and_strip, 'autorisation parentale de père'),
205
            ('DATENAISPERE_DA', 'date_naissance_pere', strip_and_date, 'date de naisance du père'),
170 206
            ('DEPNAISPERE_EN', 'departement_naissance_pere', unicode_and_strip, 'département de naissance du père'),
171 207
            ('LIEUNAISPERE_CH', 'lieu_naissance_pere', unicode_and_strip, 'lieu de naissance du père'),
172 208
            ('RUEPERE_CH', 'rue_pere', unicode_and_strip, 'rue père'),
......
176 212
            ('VILLEPERE_CH', 'ville_pere', unicode_and_strip, 'ville père'),
177 213
            ('TELEPHONEPERE_CH', 'telephone_pere', unicode_and_strip, 'téléphone père'),
178 214
            ('TELEPHONE2PERE_CH', 'telephone2_pere', unicode_and_strip, 'téléphone 2 père'),
179
            ('TELPERE_LR_IN', 'tel_pere_liste_rouge_in', unicode_and_strip, 'téléphone liste rouge père'),
180
            ('TEL2PERE_LR_IN', 'tel2_pere_liste_rouge_in', unicode_and_strip, 'téléphone 2 liste rouge père'),
181
            ('TEL_LR_IN', 'tel_liste_rourge_in', unicode_and_strip, 'téléphone liste rouge'),
182
            ('TEL2_LR_IN', 'tel2_liste_rouge_in', unicode_and_strip, 'téléphone 2 liste rouge'),
215
            ('TELPERE_LR_IN', 'tel_pere_liste_rouge', unicode_and_strip, 'téléphone liste rouge père'),
216
            ('TEL2PERE_LR_IN', 'tel2_pere_liste_rouge', unicode_and_strip, 'téléphone 2 liste rouge père'),
217
            ('TEL_LR_IN', 'tel_liste_rourge', unicode_and_strip, 'téléphone liste rouge'),
218
            ('TEL2_LR_IN', 'tel2_liste_rouge', unicode_and_strip, 'téléphone 2 liste rouge'),
183 219
            ('NOMMERE_CH', 'nom_mere', unicode_and_strip, 'madame'),
184 220
            ('PRENOMMERE_CH', 'prenom_mere', unicode_and_strip, 'prénom madame'),
185
            ('AUTOPARENTALEMERE_IN', 'autoparentale_mere_in', unicode_and_strip, 'autorisation parentale mère'),
186
            ('DATENAISMERE_DA', 'date_naissance_mere_da', unicode_and_strip, 'date de naissance de la mère'),
187
            ('DEPNAISMERE_EN', 'departement_naissance_mere_en', unicode_and_strip, 'département de naissance de la mère'),
221
            ('AUTOPARENTALEMERE_IN', 'autoparentale_mere', unicode_and_strip, 'autorisation parentale mère'),
222
            ('DATENAISMERE_DA', 'date_naissance_mere', strip_and_date, 'date de naissance de la mère'),
223
            ('DEPNAISMERE_EN', 'departement_naissance_mere', unicode_and_strip, 'département de naissance de la mère'),
188 224
            ('LIEUNAISMERE_CH', 'lieu_naissance_mere', unicode_and_strip, 'lieu de naissance de la mère'),
189 225
            ('RUEMERE_CH', 'rue_mere', unicode_and_strip, 'rue mère'),
190
            ('REVMENSUELPERE_MO', 'revenu_mensuel_pere_mo', unicode_and_strip, 'revenu mensuel du père'),
226
            ('REVMENSUELPERE_MO', 'revenu_mensuel_pere', unicode_and_strip, 'revenu mensuel du père'),
191 227
            ('RUE2MERE_CH', 'rue2_mere', unicode_and_strip, 'rue 2 mère'),
192 228
            ('RUE3MERE_CH', 'rue3_mere', unicode_and_strip, 'rue 3 mère'),
193 229
            ('CODEPOSTALMERE_CH', 'code_postal_mere', unicode_and_strip, 'code postal de la mère'),
194 230
            ('VILLEMERE_CH', 'ville_mere', unicode_and_strip, 'ville de la mère'),
195
            ('REVMENSUELMERE_MO', 'revenu_mensuel_mere_mo', unicode_and_strip, 'revenu mensuel mère'),
196
            ('REVANNUELPERE_MO', 'revenu_annuel_pere_mo', unicode_and_strip, 'revenu annuel père'),
197
            ('REVANNUELMERE_MO', 'revenu_annuel_mere_mo', unicode_and_strip, 'revenu annuel mère'),
231
            ('REVMENSUELMERE_MO', 'revenu_mensuel_mere', unicode_and_strip, 'revenu mensuel mère'),
232
            ('REVANNUELPERE_MO', 'revenu_annuel_pere', unicode_and_strip, 'revenu annuel père'),
233
            ('REVANNUELMERE_MO', 'revenu_annuel_mere', unicode_and_strip, 'revenu annuel mère'),
198 234
            ('TELEPHONEMERE_CH', 'telephone_mere', unicode_and_strip, 'téléphone mère'),
199 235
            ('TELEPHONE2MERE_CH', 'telephone2_mere', unicode_and_strip, 'téléphone 2 mère'),
200
            ('TELMERE_LR_IN', 'telephone_mere_liste_rouge_in', unicode_and_strip, 'téléphone liste rouge mère'),
201
            ('TEL2MERE_LR_IN', 'telephone2_mere_liste_rouge_in', unicode_and_strip, 'téléphone 2 liste rouge mère'),
236
            ('TELMERE_LR_IN', 'telephone_mere_liste_rouge', unicode_and_strip, 'téléphone liste rouge mère'),
237
            ('TEL2MERE_LR_IN', 'telephone2_mere_liste_rouge', unicode_and_strip, 'téléphone 2 liste rouge mère'),
202 238
            ('TELECOPIEPERE_CH', 'telecopie_pere', unicode_and_strip, 'télécopie du père'),
203 239
            ('TELECOPIE2PERE_CH', 'telecopie2_pere', unicode_and_strip, 'télécopie 2 du père'),
204 240
            ('TELECOPIEMERE_CH', 'telecopie_mere', unicode_and_strip, 'télécopie de la mère'),
......
223 259
            ('TELPROFMERE_CH', 'telephone_travail_mere', unicode_and_strip, 'téléphone travail mère'),
224 260
            ('TEL2PROFMERE_CH', 'telephone2_travail_mere', unicode_and_strip, 'téléphone 2 travail mère'),
225 261
            ('TELMOBILMERE_CH', 'telephone_mobile_mere', unicode_and_strip, 'téléphone mobile mère'),
226
            ('TOTALDU_MO', 'total_du_mo', unicode_and_strip, 'total dû'),
227
            ('TOTALREGLE_MO', 'total_regle_mo', unicode_and_strip, 'total réglé'),
262
            ('TOTALDU_MO', 'total_du', unicode_and_strip, 'total dû'),
263
            ('TOTALREGLE_MO', 'total_regle', unicode_and_strip, 'total réglé'),
228 264
            ('NUMCENTRESS_CH', 'num_centre_securite_sociale', unicode_and_strip, 'n° centre sécurité sociale'),
229 265
            ('NOMCENTRESS_CH', 'nom_centre_securite_sociale', unicode_and_strip, 'nom centre sécurité sociale'),
230 266
            ('NUMASSURANCE_CH', 'num_assurance', unicode_and_strip, 'n° assurance'),
231 267
            ('NOMASSURANCE_CH', 'nom_assurance', unicode_and_strip, 'nom assurance'),
232
            ('RIVOLI_EN', 'code_rivoli_en', unicode_and_strip, 'identifiant code rivoli'),
268
            ('RIVOLI_EN', 'code_rivoli', unicode_and_strip, 'identifiant code rivoli'),
233 269
            ('NUMCOMPTE_CH', 'numero_compte_comptable', unicode_and_strip, 'n° compte comptable'),
234 270
            ('EMAILPERE_CH', 'email_pere', unicode_and_strip, 'email du père'),
235 271
            ('EMAILMERE_CH', 'email_mere', unicode_and_strip, 'email de la mère'),
236 272
            ('NUMALLOCATAIRE_CH', 'numero_allocataire', unicode_and_strip, 'n° allocataire'),
237
            ('COMMENTAIRE_ME', 'commentaire_me', unicode_and_strip, 'commentaires / notes'),
273
            ('COMMENTAIRE_ME', 'commentaire', unicode_and_strip, 'commentaires / notes'),
238 274
            ('IDCSPPERE', 'identifiant_csp_pere', unicode_and_strip, 'référence identifiant csp'),
239 275
            ('IDCSPMERE', 'identifiant_csp_mere', unicode_and_strip, 'référence identifiant csp'),
240 276
            ('IDSECTEURS', 'identifiant_secteurs', unicode_and_strip, 'référence identifiant secteurs'),
......
268 304
            ('NUMRUEPERE_CH', 'numero_rue_pere', unicode_and_strip, 'numéro de rue père'),
269 305
            ('NUMRUEMERE_CH', 'numero_rue_mere', unicode_and_strip, 'numéro de rue mère'),
270 306
            ('IDPORTAIL_FAMILLES', 'identifiant_portail_familles', unicode_and_strip, 'identifiant de portail_familles'),
271
            ('ECHEANCEASSURANCE_DA', 'echeance_assurance_da', unicode_and_strip, 'date echéance assurance'),
272
            ('RM_MIKADO_MO', 'rm_mikado_mo', unicode_and_strip, 'revenus mensuels mikado'),
273
            ('RA_MIKADO_MO', 'ra_mikado_mo', unicode_and_strip, 'revenus annuels mikado'),
274
            ('QF_MIKADO_MO', 'qf_mikado_mo', unicode_and_strip, 'quotient familial mikado'),
275
            ('RM_DIABOLO_MO', 'rm_diabolo_mo', unicode_and_strip, 'revenus mensuels diabolo'),
276
            ('RA_DIABOLO_MO', 'ra_diabolo_mo', unicode_and_strip, 'revenus annuels diabolo'),
277
            ('QF_DIABOLO_MO', 'qf_diabolo_mo', unicode_and_strip, 'quotient familial diabolo'),
278
            ('RM_OLIGO_MO', 'rm_oligo_mo', unicode_and_strip, 'revenus mensuels oligo'),
279
            ('RA_OLIGO_MO', 'ra_oligo_mo', unicode_and_strip, 'revenus annuels oligo'),
280
            ('QF_OLIGO_MO', 'qf_oligo_mo', unicode_and_strip, 'quotient familial oligo'),
281
            ('APPLICATION_REV_MIKADO_DA', 'application_rev_mikado_da', unicode_and_strip, 'date d\'application des revenus de mikado'),
282
            ('APPLICATION_REV_DIABOLO_DA', 'application_rev_diabolo_da', unicode_and_strip, 'date d\'application des revenus de diabolo'),
283
            ('APPLICATION_REV_OLIGO_DA', 'application_rev_oligo_da', unicode_and_strip, 'date d\'application des revenus de oligo'),
307
            ('ECHEANCEASSURANCE_DA', 'echeance_assurance', unicode_and_strip, 'date echéance assurance'),
308
            ('RM_MIKADO_MO', 'rm_mikado', unicode_and_strip, 'revenus mensuels mikado'),
309
            ('RA_MIKADO_MO', 'ra_mikado', unicode_and_strip, 'revenus annuels mikado'),
310
            ('QF_MIKADO_MO', 'qf_mikado', unicode_and_strip, 'quotient familial mikado'),
311
            ('RM_DIABOLO_MO', 'rm_diabolo', unicode_and_strip, 'revenus mensuels diabolo'),
312
            ('RA_DIABOLO_MO', 'ra_diabolo', unicode_and_strip, 'revenus annuels diabolo'),
313
            ('QF_DIABOLO_MO', 'qf_diabolo', unicode_and_strip, 'quotient familial diabolo'),
314
            ('RM_OLIGO_MO', 'rm_oligo', unicode_and_strip, 'revenus mensuels oligo'),
315
            ('RA_OLIGO_MO', 'ra_oligo', unicode_and_strip, 'revenus annuels oligo'),
316
            ('QF_OLIGO_MO', 'qf_oligo', unicode_and_strip, 'quotient familial oligo'),
317
            ('APPLICATION_REV_MIKADO_DA', 'application_rev_mikado', unicode_and_strip, 'date d\'application des revenus de mikado'),
318
            ('APPLICATION_REV_DIABOLO_DA', 'application_rev_diabolo', unicode_and_strip, 'date d\'application des revenus de diabolo'),
319
            ('APPLICATION_REV_OLIGO_DA', 'application_rev_oligo', unicode_and_strip, 'date d\'application des revenus de oligo'),
284 320
    )
285 321

  
286 322
    def __init__(self, *args, **kwargs):
......
302 338
    def add_child(self, child):
303 339
        if hasattr(self, 'id'):
304 340
            child.id_famille = self.id
341
            child.client = self.client
305 342
        self.children.append(child)
306 343

  
307 344
    def save(self):
308 345
        if hasattr(self, 'id'):
309 346
            self.client.update_family(self)
310 347
        else:
348
            self.code_interne = self.client.new_code_interne()
311 349
            self.id = self.client.add_family(self)
312 350
        for child in self.children:
313 351
            child.id_famille = self.id
314 352
            child.save()
353
        self.client.clear_cache()
315 354

  
316 355
class Invoice(SimpleObject):
317 356
    COLUMNS = (
......
359 398
        self.domain = domain
360 399
        self.login = login
361 400
        self.password = password
362
        self.client = Client(url, location=location, timeout=5)
401
        self.client = Client(url, location=location, timeout=60)
363 402
        self.client.options.cache.setduration(seconds=60)
364 403

  
365 404
    def clear_cache(self):
......
373 412
        '''Call SOAP method named function_name passing args list as parameters.
374 413

  
375 414
           Any error is converted into the DominoException class.'''
415
        print 'call', function_name, args
376 416

  
377 417
        try:
378 418
            logger.debug('soap call to %s%r', function_name, args)
......
381 421
            self.data = data
382 422
        except IOError, e:
383 423
            raise DominoException('Erreur IO', e)
424
        if data is None:
425
           data = ''
384 426
        if data.startswith('ERREUR'):
385 427
            raise DominoException(data[9:].encode('utf8'))
386 428
        return data
......
488 530
                args=(id_famille, (','.join([x[0] for x in columns]))))
489 531
        return dict([(int(x), x) for x in children])
490 532

  
533
    def get_urgent_contacts(self, id_enfant):
534
        columns = UrgentContact.COLUMNS
535
        urgent_contacts = self('LISTER_PERSONNES_URGENCE',
536
                UrgentContact,
537
                
538
                args=((id_enfant, ','.join([x[0] for x in columns]))))
539
        return dict([(int(x), x) for x in urgent_contacts])
491 540

  
492 541
    @property
493 542
    @object_cached
......
502 551
            invoice.famille = self.families[invoice.id_famille]
503 552
        return invoices
504 553

  
554
    def new_code_interne(self):
555
        max_ci = 0
556
        for family in self.families.values():
557
            try:
558
                max_ci = max(max_ci, int(family.code_interne))
559
            except:
560
                pass
561
        return '%05d' % (max_ci+1)
562

  
505 563
    def get_invoices(self, id_famille=0, state='TOUTES'):
506 564
        '''Get invoices informations.
507 565

  
......
551 609
                return famille
552 610
        return None
553 611

  
612
    def get_family_by_code_interne(self, code_interne):
613
        '''Return the first whose one email attribute matches the given email'''
614
        for famille in self.families.values():
615
            if getattr(famille, 'code_interne', None) == code_interne:
616
                return famille
617
        return None
618

  
554 619
    def pay_invoice(self, id_invoices, amount, other_information, date=None):
555 620
        '''Notify Domino of the payment of some invoices.
556 621

  

Also available in: Unified diff