1
|
import sys
|
2
|
from datetime import datetime
|
3
|
import collections
|
4
|
from decimal import Decimal
|
5
|
|
6
|
from qommon.cron import CronJob
|
7
|
from qommon.publisher import get_publisher_class
|
8
|
from qommon import get_logger
|
9
|
|
10
|
from wcs.users import User
|
11
|
|
12
|
from abelium_domino_ui import (get_client, is_activated, get_invoice_regie,
|
13
|
abelium_domino_ws)
|
14
|
from payments import Invoice, Transaction
|
15
|
|
16
|
DOMINO_ID_PREFIX = 'DOMINO-'
|
17
|
|
18
|
def synchronize_domino(publisher):
|
19
|
regie = get_invoice_regie(publisher)
|
20
|
logger = get_logger()
|
21
|
if not is_activated(publisher) or not regie:
|
22
|
return
|
23
|
client = get_client(publisher)
|
24
|
if client is None:
|
25
|
logger.warning('Unable to create a DominoWS object')
|
26
|
return
|
27
|
client.clear_cache()
|
28
|
users = User.values()
|
29
|
users_by_mail = dict(((user.email, user) for user in users))
|
30
|
users_by_code_interne = {}
|
31
|
for user in users:
|
32
|
if hasattr(user, 'abelium_domino_code_famille'):
|
33
|
users_by_code_interne[user.abelium_domino_code_famille] = user
|
34
|
try:
|
35
|
invoices = client.invoices
|
36
|
except abelium_domino_ws.DominoException, e:
|
37
|
publisher.notify_of_exception(sys.exc_info(), context='[DOMINO]')
|
38
|
logger.error('domino cron: failure to retrieve invoice list from domino '
|
39
|
'for synchronization [error:%s]', e)
|
40
|
return
|
41
|
# import new invoices
|
42
|
logger.info('domino cron: retrieved %i invoices', len(invoices))
|
43
|
for invoice_id, invoice in invoices.iteritems():
|
44
|
user = None
|
45
|
if invoice.family.code_interne in users_by_code_interne:
|
46
|
user = users_by_code_interne[invoice.family.code_interne]
|
47
|
if user is None:
|
48
|
for email in (invoice.family.email_pere, invoice.family.email_mere,
|
49
|
invoice.family.adresse_internet):
|
50
|
user = users_by_mail.get(email)
|
51
|
if user:
|
52
|
break
|
53
|
else:
|
54
|
continue
|
55
|
external_id = '%s%s' % (DOMINO_ID_PREFIX, invoice.id)
|
56
|
payment_invoice = Invoice.get_on_index(external_id, 'external_id', ignore_errors=True)
|
57
|
if payment_invoice:
|
58
|
continue
|
59
|
if invoice.reste_du == Decimal(0) or invoice.reste_du < Decimal(0):
|
60
|
continue
|
61
|
payment_invoice = Invoice()
|
62
|
payment_invoice.user_id = user.id
|
63
|
payment_invoice.external_id = external_id
|
64
|
payment_invoice.regie_id = regie.id
|
65
|
payment_invoice.formdef_id = None
|
66
|
payment_invoice.formdata_id = None
|
67
|
payment_invoice.amount = invoice.reste_du
|
68
|
payment_invoice.date = invoice.creation
|
69
|
payment_invoice.domino_synchro_date = datetime.now()
|
70
|
if 'etablissement' in invoice._detail:
|
71
|
etablissement = invoice._detail['etablissement'].encode('utf-8')
|
72
|
payment_invoice.subject = _('%s - Childcare services') % etablissement
|
73
|
else:
|
74
|
payment_invoice.subject = _('Childcare services')
|
75
|
if invoice._detail.get('lignes'):
|
76
|
details = []
|
77
|
details.append('<table class="invoice-details"><thead>')
|
78
|
tpl = '''<tr>
|
79
|
<td>%(designation)s</td>
|
80
|
<td>%(quantite)s</td>
|
81
|
<td>%(prix)s</td>
|
82
|
<td>%(montant)s</td>
|
83
|
</tr>'''
|
84
|
captions = {
|
85
|
'designation': _('Caption'),
|
86
|
'quantite': _('Quantity'),
|
87
|
'prix': _('Price'),
|
88
|
'amount': _('Amount')
|
89
|
}
|
90
|
details.append(tpl % captions)
|
91
|
details.append('</thead>')
|
92
|
details.append('<tbody>')
|
93
|
for ligne in invoice._detail['lignes']:
|
94
|
def encode(x):
|
95
|
a, b = x
|
96
|
b = b.encode('utf-8')
|
97
|
return (a,b)
|
98
|
ligne = map(encode, ligne)
|
99
|
ligne = dict(ligne)
|
100
|
base = collections.defaultdict(lambda:'')
|
101
|
base.update(ligne)
|
102
|
details.append(tpl % base)
|
103
|
details.append('</tbody></table>')
|
104
|
payment_invoice.details = '\n'.join(details)
|
105
|
payment_invoice.store()
|
106
|
logger.info('domino cron: remote invoice %s for family %s added to user %s invoices with id %s',
|
107
|
invoice.id, invoice.family.id, user.id, payment_invoice.id)
|
108
|
|
109
|
# update invoices
|
110
|
invoices_ids = dict(invoices.iteritems())
|
111
|
for payment_invoice in Invoice.values():
|
112
|
if payment_invoice.external_id is None or not payment_invoice.external_id.startswith(DOMINO_ID_PREFIX):
|
113
|
continue # not a payment related to domino we skip
|
114
|
i = payment_invoice.external_id[len(DOMINO_ID_PREFIX):]
|
115
|
i = int(i)
|
116
|
invoice = invoices_ids.get(i)
|
117
|
if payment_invoice.paid:
|
118
|
if not invoice:
|
119
|
# invoice has been paid (locally or not) but remote invoice has
|
120
|
# been deleted do, we do nothing.
|
121
|
continue
|
122
|
if getattr(payment_invoice, 'domino_knows_its_paid', None) or getattr(payment_invoice, 'paid_by_domino', None):
|
123
|
# synchronization of payment already done, skip
|
124
|
continue
|
125
|
transactions = Transaction.get_with_indexed_value('invoice_ids', payment_invoice.id)
|
126
|
if not transactions:
|
127
|
logger.warning("domino cron: invoice %s is marked paid but does "
|
128
|
"not have any linked transaction.", payment_invoice.id)
|
129
|
details = '' # no details about the payment, problem
|
130
|
else:
|
131
|
details = repr(transactions[0].__dict__)
|
132
|
if invoice.montant != payment_invoice.amount:
|
133
|
pass # add warning logs
|
134
|
try:
|
135
|
client.pay_invoice([invoice], invoice.montant, details,
|
136
|
payment_invoice.paid_date)
|
137
|
except abelium_domino_ws.DominoException, e:
|
138
|
logger.error('domino cron: invoice %s has been paid, but the remote system '
|
139
|
'is unreachable, notification will be done again '
|
140
|
'later [error: %s]', invoice.id, e)
|
141
|
else:
|
142
|
# memorize the date of synchronization
|
143
|
payment_invoice.domino_knows_its_paid = datetime.now()
|
144
|
payment_invoice.store()
|
145
|
logger.info('domino cron: domino: invoice %s has been paid; remote system has been '
|
146
|
'notified', payment_invoice.id)
|
147
|
else: # unpaid
|
148
|
if not invoice:
|
149
|
logger.info('domino cron: remote invoice %s disapearred, so its '
|
150
|
'still-unpaid local counterpart invoice %s was deleted.',
|
151
|
i, payment_invoice.id)
|
152
|
payment_invoice.remove_self()
|
153
|
elif invoice.paid():
|
154
|
payment_invoice.paid_by_domino = True
|
155
|
payment_invoice.pay()
|
156
|
logger.info('domino cron: remote invoice %s has beend paid, '
|
157
|
'local invoice %s of user %s is now marked as paid.',
|
158
|
invoice.id, payment_invoice.id, payment_invoice.user_id)
|
159
|
else: # not invoice.paid()
|
160
|
pass # still waiting for the payment
|
161
|
|
162
|
get_publisher_class().register_cronjob(CronJob(function=synchronize_domino,
|
163
|
hours=range(0, 24), minutes=range(0, 60, 30)))
|