16 |
16 |
# You should have received a copy of the GNU Affero General Public License
|
17 |
17 |
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
18 |
18 |
|
19 |
|
from datetime import date, time
|
|
19 |
from datetime import date
|
20 |
20 |
import os
|
21 |
|
import shutil
|
22 |
21 |
import sys
|
23 |
22 |
import zipfile
|
24 |
23 |
|
... | ... | |
30 |
29 |
from django.utils.translation import ugettext_lazy as _
|
31 |
30 |
from django.http import Http404, HttpResponse
|
32 |
31 |
from django.db import models, transaction
|
33 |
|
from django.utils.timezone import make_aware, datetime, get_current_timezone, now, is_naive
|
|
32 |
from django.utils.timezone import make_aware, datetime, now, is_naive
|
34 |
33 |
|
35 |
34 |
from passerelle.base.models import BaseResource
|
36 |
35 |
from passerelle.compat import json_loads
|
... | ... | |
73 |
72 |
address += ', %(zipcode)s %(city)s' % data
|
74 |
73 |
return address
|
75 |
74 |
|
|
75 |
|
76 |
76 |
def format_family(family):
|
77 |
77 |
data = {'quotient': family.family_quotient}
|
78 |
78 |
for attr in ('street_name', 'street_number', 'address_complement',
|
... | ... | |
81 |
81 |
data['address'] = format_address(data)
|
82 |
82 |
return data
|
83 |
83 |
|
|
84 |
|
84 |
85 |
def format_person(p):
|
85 |
86 |
data = {'id': str(p.id),
|
86 |
87 |
'text': p.fullname,
|
... | ... | |
94 |
95 |
data['address'] = format_address(data)
|
95 |
96 |
return data
|
96 |
97 |
|
|
98 |
|
97 |
99 |
def format_invoice(i):
|
98 |
|
invoice = {'id': i.external_id,
|
99 |
|
'label': i.label,
|
100 |
|
'amount': i.amount,
|
101 |
|
'total_amount': i.total_amount,
|
102 |
|
'created': i.issue_date,
|
103 |
|
'pay_limit_date': i.pay_limit_date,
|
104 |
|
'payment_date': i.payment_date,
|
105 |
|
'paid': i.paid,
|
106 |
|
'online_payment': i.online_payment,
|
107 |
|
'no_online_payment_reason': i.no_online_payment_reason,
|
108 |
|
'has_pdf': i.has_pdf}
|
|
100 |
invoice = {
|
|
101 |
'id': i.external_id,
|
|
102 |
'label': i.label,
|
|
103 |
'amount': i.amount,
|
|
104 |
'total_amount': i.total_amount,
|
|
105 |
'created': i.issue_date,
|
|
106 |
'pay_limit_date': i.pay_limit_date,
|
|
107 |
'payment_date': i.payment_date,
|
|
108 |
'paid': i.paid,
|
|
109 |
'online_payment': i.online_payment,
|
|
110 |
'no_online_payment_reason': i.no_online_payment_reason,
|
|
111 |
'has_pdf': i.has_pdf
|
|
112 |
}
|
109 |
113 |
if now().date() > i.pay_limit_date:
|
110 |
114 |
invoice['online_payment'] = False
|
111 |
115 |
invoice['no_online_payment_reason'] = 'past-due-date'
|
112 |
116 |
return invoice
|
113 |
117 |
|
|
118 |
|
114 |
119 |
class FileNotFoundError(Exception):
|
115 |
120 |
http_status = 200
|
116 |
121 |
log_error = False
|
... | ... | |
122 |
127 |
old_key = new_key = attribute
|
123 |
128 |
if isinstance(attribute, tuple):
|
124 |
129 |
old_key, new_key = attribute
|
125 |
|
if not old_key in d1:
|
|
130 |
if old_key not in d1:
|
126 |
131 |
continue
|
127 |
132 |
d2[new_key] = d1[old_key]
|
128 |
133 |
return d2
|
129 |
134 |
|
|
135 |
|
130 |
136 |
class GenericFamily(BaseResource):
|
131 |
137 |
category = _('Business Process Connectors')
|
132 |
138 |
archive = models.FileField(_('Data Archive'), upload_to='archives', max_length=256)
|
133 |
139 |
file_format = models.CharField(
|
134 |
|
_('File Format'), max_length=40,
|
135 |
|
choices=(
|
136 |
|
('native', _('Native')),
|
137 |
|
('concerto_fondettes', _('Concerto extract from Fondettes')),
|
138 |
|
('concerto_orleans', _(u'Concerto extract from Orléans')),
|
139 |
|
),
|
140 |
|
default='native')
|
|
140 |
_('File Format'), max_length=40,
|
|
141 |
choices=(
|
|
142 |
('native', _('Native')),
|
|
143 |
('concerto_fondettes', _('Concerto extract from Fondettes')),
|
|
144 |
('concerto_orleans', _(u'Concerto extract from Orléans')),
|
|
145 |
),
|
|
146 |
default='native')
|
141 |
147 |
|
142 |
148 |
class Meta:
|
143 |
149 |
verbose_name = _('Generic Family Connector')
|
... | ... | |
191 |
197 |
families.append(family_data['id'])
|
192 |
198 |
address = family_data.get('address') or {}
|
193 |
199 |
family_data.update(address)
|
194 |
|
data = dict_cherry_pick(family_data,
|
195 |
|
('login',
|
196 |
|
'password',
|
197 |
|
'family_quotient',
|
198 |
|
('number', 'street_number'),
|
199 |
|
('postal_code', 'zipcode'),
|
200 |
|
('street', 'street_name'),
|
201 |
|
('complement', 'address_complement')))
|
202 |
|
family, created = Family.objects.update_or_create(external_id=family_data['id'],
|
203 |
|
resource=self, defaults=data)
|
|
200 |
data = dict_cherry_pick(
|
|
201 |
family_data,
|
|
202 |
('login',
|
|
203 |
'password',
|
|
204 |
'family_quotient',
|
|
205 |
('number', 'street_number'),
|
|
206 |
('postal_code', 'zipcode'),
|
|
207 |
('street', 'street_name'),
|
|
208 |
('complement', 'address_complement')))
|
|
209 |
family, created = Family.objects.update_or_create(
|
|
210 |
external_id=family_data['id'], resource=self, defaults=data)
|
204 |
211 |
for adult in family_data.get('adults') or []:
|
205 |
212 |
adults.append(adult['id'])
|
206 |
213 |
adult_address = adult.get('address') or {}
|
207 |
214 |
adult.update(adult_address)
|
208 |
|
data = dict_cherry_pick(adult,
|
209 |
|
('first_name',
|
210 |
|
'last_name',
|
211 |
|
'phone',
|
212 |
|
('mobile', 'cellphone'),
|
213 |
|
'sex',
|
214 |
|
('number', 'street_number'),
|
215 |
|
('postal_code', 'zipcode'),
|
216 |
|
('street', 'street_name'),
|
217 |
|
('complement', 'address_complement'),
|
218 |
|
'country')
|
219 |
|
)
|
220 |
|
Adult.objects.update_or_create(family=family,
|
221 |
|
external_id=adult['id'], defaults=data)
|
|
215 |
data = dict_cherry_pick(
|
|
216 |
adult,
|
|
217 |
('first_name',
|
|
218 |
'last_name',
|
|
219 |
'phone',
|
|
220 |
('mobile', 'cellphone'),
|
|
221 |
'sex',
|
|
222 |
('number', 'street_number'),
|
|
223 |
('postal_code', 'zipcode'),
|
|
224 |
('street', 'street_name'),
|
|
225 |
('complement', 'address_complement'),
|
|
226 |
'country'))
|
|
227 |
Adult.objects.update_or_create(
|
|
228 |
family=family, external_id=adult['id'], defaults=data)
|
222 |
229 |
# cleanup adults
|
223 |
230 |
Adult.objects.exclude(external_id__in=adults).delete()
|
224 |
231 |
|
225 |
232 |
for child in family_data.get('children') or []:
|
226 |
233 |
children.append(child['id'])
|
227 |
|
data = dict_cherry_pick(child,
|
228 |
|
('first_name', 'last_name', 'sex', 'birthdate'))
|
|
234 |
data = dict_cherry_pick(child, ('first_name', 'last_name', 'sex', 'birthdate'))
|
229 |
235 |
Child.objects.get_or_create(family=family,
|
230 |
|
external_id=child['id'], defaults=data)
|
|
236 |
external_id=child['id'],
|
|
237 |
defaults=data)
|
231 |
238 |
# cleanup children
|
232 |
239 |
Child.objects.exclude(external_id__in=children).delete()
|
233 |
240 |
|
234 |
241 |
for invoice in family_data['invoices']:
|
235 |
242 |
invoices.append(invoice['id'])
|
236 |
|
data = dict_cherry_pick(invoice,
|
237 |
|
('label',
|
238 |
|
('created', 'issue_date'),
|
239 |
|
'pay_limit_date',
|
240 |
|
'litigation_date', 'total_amount', 'payment_date',
|
241 |
|
'amount', 'autobilling'))
|
|
243 |
data = dict_cherry_pick(
|
|
244 |
invoice,
|
|
245 |
('label',
|
|
246 |
('created', 'issue_date'),
|
|
247 |
'pay_limit_date',
|
|
248 |
'litigation_date', 'total_amount', 'payment_date',
|
|
249 |
'amount', 'autobilling'))
|
242 |
250 |
for date_attribute in data.keys():
|
243 |
251 |
if not date_attribute.endswith('_date'):
|
244 |
252 |
continue
|
... | ... | |
248 |
256 |
data[date_attribute] = get_date(data[date_attribute])
|
249 |
257 |
data['paid'] = bool(data.get('payment_date'))
|
250 |
258 |
Invoice.objects.update_or_create(resource=self,
|
251 |
|
family=family, external_id=invoice['id'], defaults=data)
|
|
259 |
family=family,
|
|
260 |
external_id=invoice['id'],
|
|
261 |
defaults=data)
|
252 |
262 |
if 'invoices/%s.pdf' % invoice['id'] in archive_files:
|
253 |
263 |
with open(os.path.join(invoices_dir, '%s.pdf' % invoice['id']), 'wb') as fp:
|
254 |
264 |
fp.write(archive.read('invoices/%s.pdf' % invoice['id']))
|
... | ... | |
257 |
267 |
Invoice.objects.exclude(external_id__in=invoices).delete()
|
258 |
268 |
for filename in os.listdir(invoices_dir):
|
259 |
269 |
file_invoice_id = os.path.splitext(filename)[0]
|
260 |
|
if not file_invoice_id in invoices:
|
|
270 |
if file_invoice_id not in invoices:
|
261 |
271 |
os.unlink(os.path.join(invoices_dir, filename))
|
262 |
272 |
|
263 |
273 |
# cleanup families
|
... | ... | |
348 |
358 |
return None
|
349 |
359 |
|
350 |
360 |
@endpoint(name='regie', perm='can_access',
|
351 |
|
pattern='^invoice/(?P<invoice_id>\w+)/$')
|
|
361 |
pattern=r'^invoice/(?P<invoice_id>\w+)/$')
|
352 |
362 |
def get_invoice_details(self, request, invoice_id, NameID=None, email=None, **kwargs):
|
353 |
363 |
invoice = self.get_invoice(invoice_id)
|
354 |
364 |
if not invoice:
|
... | ... | |
356 |
366 |
return {'data': format_invoice(invoice)}
|
357 |
367 |
|
358 |
368 |
@endpoint(name='regie', perm='can_access',
|
359 |
|
pattern='^invoice/(?P<invoice_id>\w+)/pdf/$')
|
|
369 |
pattern=r'^invoice/(?P<invoice_id>\w+)/pdf/$')
|
360 |
370 |
def get_invoice_pdf(self, request, invoice_id, **kwargs):
|
361 |
371 |
invoice = self.get_invoice(invoice_id)
|
362 |
372 |
if not invoice:
|
... | ... | |
364 |
374 |
return invoice.get_pdf()
|
365 |
375 |
|
366 |
376 |
@endpoint(name='regie', methods=['post'],
|
367 |
|
perm='can_access', pattern='^invoice/(?P<invoice_id>\w+)/pay/$')
|
|
377 |
perm='can_access', pattern=r'^invoice/(?P<invoice_id>\w+)/pay/$')
|
368 |
378 |
def pay_invoice(self, request, invoice_id, **kwargs):
|
369 |
379 |
data = json_loads(request.body)
|
370 |
380 |
invoice = self.get_invoice(invoice_id)
|
... | ... | |
381 |
391 |
@endpoint(name='regie', perm='can_access', pattern='^users/with-pending-invoices/$')
|
382 |
392 |
def get_pending_invoices_by_nameid(self, request):
|
383 |
393 |
data = defaultdict(lambda: {'invoices': []})
|
384 |
|
for i in Invoice.objects.filter(payment_date__isnull=True,
|
385 |
|
family__resource=self,
|
386 |
|
family__familylink__isnull=False).select_related('family').prefetch_related('family__familylink_set'):
|
|
394 |
for i in (Invoice.objects.filter(
|
|
395 |
payment_date__isnull=True,
|
|
396 |
family__resource=self,
|
|
397 |
family__familylink__isnull=False)
|
|
398 |
.select_related('family')
|
|
399 |
.prefetch_related('family__familylink_set')):
|
387 |
400 |
name_id = i.family.familylink_set.all()[0].name_id
|
388 |
401 |
data[name_id]['invoices'].append(format_invoice(i))
|
389 |
402 |
return {'data': data}
|
390 |
|
-
|