0002-caluire-axel-add-invoices_history-endpoint-53884.patch
passerelle/contrib/caluire_axel/models.py | ||
---|---|---|
563 | 563 | |
564 | 564 |
data = result.json_response['DATA']['PORTAIL']['GETFACTURESAPAYER'] |
565 | 565 |
result = [] |
566 | 566 | |
567 | 567 |
for facture in data.get('FACTURE', []): |
568 | 568 |
result.append(utils.normalize_invoice(facture, family_id)) |
569 | 569 |
return result |
570 | 570 | |
571 |
def get_historical_invoices(self, regie_id, family_id, nb_mounts_limit): |
|
572 |
try: |
|
573 |
nb_mois = int(nb_mounts_limit) |
|
574 |
except ValueError: |
|
575 |
raise APIError('nb_mounts_limit must be an integer', err_code='bad-request', http_status=400) |
|
576 |
try: |
|
577 |
result = schemas.get_list_factures( |
|
578 |
self, |
|
579 |
{ |
|
580 |
'PORTAIL': { |
|
581 |
'GETLISTFACTURES': { |
|
582 |
'IDENTFAMILLE': family_id, |
|
583 |
'IDENTREGIEFACT': regie_id, |
|
584 |
'NBMOIS': nb_mois, |
|
585 |
} |
|
586 |
}, |
|
587 |
}, |
|
588 |
) |
|
589 |
except axel.AxelError as e: |
|
590 |
raise APIError( |
|
591 |
'Axel error: %s' % e, |
|
592 |
err_code='error', |
|
593 |
data={'xml_request': e.xml_request, 'xml_response': e.xml_response}, |
|
594 |
) |
|
595 | ||
596 |
code = result.json_response['DATA']['PORTAIL']['GETLISTFACTURES']['CODE'] |
|
597 |
if code < 0: |
|
598 |
raise APIError( |
|
599 |
'Wrong get-historical-invoices status', |
|
600 |
err_code='get-historical-invoices-code-error-%s' % code, |
|
601 |
) |
|
602 | ||
603 |
data = result.json_response['DATA']['PORTAIL']['GETLISTFACTURES'] |
|
604 |
result = [] |
|
605 |
for facture in data.get('FACTURE', []): |
|
606 |
result.append(utils.normalize_invoice(facture, family_id, historical=True)) |
|
607 |
return result |
|
608 | ||
571 | 609 |
@endpoint( |
572 | 610 |
display_category=_('Invoices'), |
573 | 611 |
display_order=1, |
574 | 612 |
name='regie', |
575 | 613 |
perm='can_access', |
576 | 614 |
pattern=r'^(?P<regie_id>[\w-]+)/invoices/?$', |
577 | 615 |
example_pattern='{regie_id}/invoices', |
578 | 616 |
description=_("Get invoices to pay"), |
... | ... | |
581 | 619 |
'regie_id': {'description': _('Regie identifier'), 'example_value': 'ENF'}, |
582 | 620 |
}, |
583 | 621 |
) |
584 | 622 |
def invoices(self, request, regie_id, NameID): |
585 | 623 |
link = self.get_link(NameID) |
586 | 624 |
invoices_data = self.get_invoices(regie_id=regie_id, family_id=link.family_id) |
587 | 625 |
return {'data': invoices_data} |
588 | 626 | |
627 |
@endpoint( |
|
628 |
display_category=_('Invoices'), |
|
629 |
display_order=2, |
|
630 |
name='regie', |
|
631 |
perm='can_access', |
|
632 |
pattern=r'^(?P<regie_id>[\w-]+)/invoices/history/?$', |
|
633 |
example_pattern='{regie_id}/invoices/history', |
|
634 |
description=_("Get invoices already paid"), |
|
635 |
parameters={ |
|
636 |
'NameID': {'description': _('Publik ID')}, |
|
637 |
'regie_id': {'description': _('Regie identifier'), 'example_value': 'ENF'}, |
|
638 |
'nb_mounts_limit': {'description': _('Number of months of history'), 'example_value': '3'}, |
|
639 |
}, |
|
640 |
) |
|
641 |
def invoices_history(self, request, regie_id, NameID, nb_mounts_limit='3'): |
|
642 |
link = self.get_link(NameID) |
|
643 |
invoices_data = self.get_historical_invoices( |
|
644 |
regie_id, family_id=link.family_id, nb_mounts_limit=nb_mounts_limit |
|
645 |
) |
|
646 |
return {'data': invoices_data} |
|
647 | ||
589 | 648 | |
590 | 649 |
class Link(models.Model): |
591 | 650 |
resource = models.ForeignKey(CaluireAxel, on_delete=models.CASCADE) |
592 | 651 |
name_id = models.CharField(blank=False, max_length=256) |
593 | 652 |
family_id = models.CharField(blank=False, max_length=128) |
594 | 653 |
person_id = models.CharField(blank=False, max_length=128) |
595 | 654 | |
596 | 655 |
class Meta: |
passerelle/contrib/caluire_axel/schemas.py | ||
---|---|---|
75 | 75 |
find_individus = Operation('FindIndividus') |
76 | 76 |
get_famille_individus = Operation('GetFamilleIndividus') |
77 | 77 |
get_individu = Operation('GetIndividu') |
78 | 78 |
get_list_ecole = Operation('GetListEcole') |
79 | 79 |
get_list_activites = Operation('GetListActivites') |
80 | 80 |
create_inscription_activite = Operation('CreateInscriptionActivite', data_method='setData') |
81 | 81 |
get_agenda = Operation('GetAgenda') |
82 | 82 |
get_factures_a_payer = Operation('GetFacturesaPayer') |
83 |
get_list_factures = Operation('GetListFactures') |
|
83 | 84 | |
84 | 85 | |
85 | 86 |
LINK_SCHEMA = copy.deepcopy( |
86 | 87 |
find_individus.request_schema['properties']['PORTAIL']['properties']['FINDINDIVIDU'] |
87 | 88 |
) |
88 | 89 |
for key in ['NAISSANCE', 'CODEPOSTAL', 'VILLE', 'TEL', 'MAIL']: |
89 | 90 |
LINK_SCHEMA['properties'].pop(key) |
90 | 91 |
LINK_SCHEMA['required'].remove(key) |
passerelle/contrib/caluire_axel/xsd/Q_GetListFactures.xsd | ||
---|---|---|
1 |
<?xml version="1.0" encoding="utf-8" ?> |
|
2 |
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:all="urn:AllAxelTypes"> |
|
3 |
|
|
4 |
<xsd:import schemaLocation="./AllAxelTypes.xsd" namespace="urn:AllAxelTypes" /> |
|
5 |
|
|
6 |
<xsd:complexType name="PORTAILType"> |
|
7 |
<xsd:sequence> |
|
8 |
<xsd:element ref="GETLISTFACTURES" minOccurs="0" maxOccurs="1"/> |
|
9 |
</xsd:sequence> |
|
10 |
</xsd:complexType> |
|
11 |
|
|
12 |
<xsd:complexType name="GETLISTFACTURESType"> |
|
13 |
<xsd:sequence> |
|
14 |
<xsd:element ref="IDENTFAMILLE"/> |
|
15 |
<xsd:element ref="IDENTREGIEFACT"/> |
|
16 |
<xsd:element ref="NBMOIS"/> |
|
17 |
</xsd:sequence> |
|
18 |
</xsd:complexType> |
|
19 |
|
|
20 |
<xsd:element name="IDENTFAMILLE" type="all:IDENTREQUIREDType"/> |
|
21 |
<xsd:element name="IDENTREGIEFACT" type="all:IDREQUIREDType"/> |
|
22 |
<xsd:element name="NBMOIS" type="xsd:unsignedInt"/> |
|
23 |
|
|
24 |
<xsd:element name="GETLISTFACTURES" type="GETLISTFACTURESType"/> |
|
25 |
|
|
26 |
<xsd:element name="PORTAIL" type="PORTAILType"/> |
|
27 |
|
|
28 |
</xsd:schema> |
passerelle/contrib/caluire_axel/xsd/R_GetListFactures.xsd | ||
---|---|---|
1 |
<?xml version="1.0" encoding="utf-8" ?> |
|
2 |
<xsd:schema xmlns:all="urn:AllAxelTypes" xmlns:ind="urn:Individu" xmlns:xsd="http://www.w3.org/2001/XMLSchema" > |
|
3 |
|
|
4 |
<xsd:import schemaLocation="./AllAxelTypes.xsd" namespace="urn:AllAxelTypes" /> |
|
5 |
|
|
6 |
<xsd:redefine schemaLocation="./R_ShemaResultat.xsd"> |
|
7 |
<xsd:simpleType name="TYPEType"> |
|
8 |
<xsd:restriction base="TYPEType"> |
|
9 |
<xsd:enumeration value="GetListFactures" /> |
|
10 |
</xsd:restriction> |
|
11 |
</xsd:simpleType> |
|
12 |
|
|
13 |
<xsd:complexType name="PORTAILType"> |
|
14 |
<xsd:complexContent> |
|
15 |
<xsd:extension base="PORTAILType"> |
|
16 |
<xsd:sequence> |
|
17 |
<xsd:element ref="GETLISTFACTURES" minOccurs="0" maxOccurs="1"/> |
|
18 |
</xsd:sequence> |
|
19 |
</xsd:extension> |
|
20 |
</xsd:complexContent> |
|
21 |
</xsd:complexType> |
|
22 |
</xsd:redefine> |
|
23 |
|
|
24 |
<xsd:complexType name="FACTUREType"> |
|
25 |
<xsd:sequence> |
|
26 |
<xsd:element ref="IDFACTURE" /> |
|
27 |
<xsd:element ref="MONTANT"/> |
|
28 |
<xsd:element ref="ENCAISSE"/> |
|
29 |
<xsd:element ref="FACTURATION"/> |
|
30 |
<xsd:element ref="EMISSION"/><!-- CUSTOM --> |
|
31 |
<xsd:element ref="ECHEANCE"/><!-- CUSTOM --> |
|
32 |
<xsd:element ref="EXISTEPDF"/> |
|
33 |
</xsd:sequence> |
|
34 |
</xsd:complexType> |
|
35 |
|
|
36 |
<xsd:complexType name="GETLISTFACTURESType"> |
|
37 |
<xsd:sequence> |
|
38 |
<xsd:element ref="CODE" /> |
|
39 |
<xsd:element ref="FACTURE" minOccurs="0" maxOccurs="unbounded" /> |
|
40 |
</xsd:sequence> |
|
41 |
</xsd:complexType> |
|
42 |
|
|
43 |
<xsd:element name="CODE" type="xsd:integer"/> |
|
44 |
<xsd:element name="IDFACTURE" type="xsd:positiveInteger"/> |
|
45 |
<xsd:element name="MONTANT" type="all:MONTANTType"/> |
|
46 |
<xsd:element name="ENCAISSE" type="all:MONTANTType"/> |
|
47 |
<xsd:element name="FACTURATION" type="all:NOMType"/> |
|
48 |
<xsd:element name="EMISSION" type="all:DATEType"/><!-- CUSTOM --> |
|
49 |
<xsd:element name="ECHEANCE" type="all:DATEType"/><!-- CUSTOM --> |
|
50 |
<xsd:element name="EXISTEPDF" type="all:ONEmptyType"/> |
|
51 |
<xsd:element name="FACTURE" type="FACTUREType"/> |
|
52 |
|
|
53 |
<xsd:element name="GETLISTFACTURES" type="GETLISTFACTURESType"/> |
|
54 |
|
|
55 |
</xsd:schema> |
tests/test_caluire_axel.py | ||
---|---|---|
1270 | 1270 |
'ENCAISSE': '0.00', |
1271 | 1271 |
'ECHEANCE': '2020-01-04', |
1272 | 1272 |
'EMISSION': '2019-12-12', |
1273 | 1273 |
'EXISTEPDF': False, |
1274 | 1274 |
} |
1275 | 1275 |
}, |
1276 | 1276 |
}, |
1277 | 1277 |
] |
1278 | ||
1279 | ||
1280 |
def test_invoices_history_endpoint_axel_error(app, resource): |
|
1281 |
Link.objects.create(resource=resource, name_id='yyy', family_id='XXX', person_id='42') |
|
1282 |
with mock.patch('passerelle.contrib.caluire_axel.schemas.get_list_factures') as operation: |
|
1283 |
operation.side_effect = AxelError('FooBar') |
|
1284 |
resp = app.get('/caluire-axel/test/regie/MAREGIE/invoices/history?NameID=yyy') |
|
1285 |
assert resp.json['err_desc'] == "Axel error: FooBar" |
|
1286 |
assert resp.json['err'] == 'error' |
|
1287 | ||
1288 |
content = '''<PORTAIL> |
|
1289 |
<GETLISTFACTURES> |
|
1290 |
<CODE>-3</CODE> |
|
1291 |
</GETLISTFACTURES> |
|
1292 |
</PORTAIL>''' |
|
1293 |
with mock_data(content, 'GetListFactures'): |
|
1294 |
resp = app.get('/caluire-axel/test/regie/MAREGIE/invoices/history?NameID=yyy') |
|
1295 |
assert resp.json['err_desc'] == "Wrong get-historical-invoices status" |
|
1296 |
assert resp.json['err'] == 'get-historical-invoices-code-error--3' |
|
1297 | ||
1298 | ||
1299 |
def test_invoices_history_endpoint_bad_request(app, resource): |
|
1300 |
Link.objects.create(resource=resource, name_id='yyy', family_id='XXX', person_id='42') |
|
1301 |
with mock_data(None, 'GetListFactures'): |
|
1302 |
resp = app.get( |
|
1303 |
'/caluire-axel/test/regie/MAREGIE/invoices/history?NameID=yyy&nb_mounts_limit=not_a_number', |
|
1304 |
status=400, |
|
1305 |
) |
|
1306 |
assert resp.json['err_desc'] == "nb_mounts_limit must be an integer" |
|
1307 |
assert resp.json['err'] == 'bad-request' |
|
1308 | ||
1309 | ||
1310 |
def test_invoices_history_endpoint_no_result(app, resource): |
|
1311 |
resp = app.get('/caluire-axel/test/regie/MAREGIE/invoices/history?NameID=yyy') |
|
1312 |
assert resp.json['err_desc'] == "Person not found" |
|
1313 |
assert resp.json['err'] == 'not-found' |
|
1314 | ||
1315 | ||
1316 |
def test_invoices_history_endpoint_no_invoice(app, resource): |
|
1317 |
Link.objects.create(resource=resource, name_id='yyy', family_id='XXX', person_id='42') |
|
1318 |
content = '''<PORTAIL> |
|
1319 |
<GETLISTFACTURES> |
|
1320 |
<CODE>0</CODE> |
|
1321 |
</GETLISTFACTURES> |
|
1322 |
</PORTAIL>''' |
|
1323 |
with mock_data(content, 'GetListFactures'): |
|
1324 |
resp = app.get('/caluire-axel/test/regie/MAREGIE/invoices/history?NameID=yyy') |
|
1325 |
assert resp.json['err'] == 0 |
|
1326 |
assert resp.json['data'] == [] |
|
1327 | ||
1328 | ||
1329 |
def test_invoices_history_endpoint(app, resource): |
|
1330 |
Link.objects.create(resource=resource, name_id='yyy', family_id='XXX', person_id='42') |
|
1331 |
filepath = os.path.join(os.path.dirname(__file__), 'data/caluire_axel/invoices.xml') |
|
1332 |
with open(filepath) as xml: |
|
1333 |
content = ( |
|
1334 |
'''<PORTAIL> |
|
1335 |
<GETLISTFACTURES> |
|
1336 |
%s |
|
1337 |
</GETLISTFACTURES> |
|
1338 |
</PORTAIL>''' |
|
1339 |
% xml.read() |
|
1340 |
) |
|
1341 |
with mock_data(content, 'GetListFactures'): |
|
1342 |
resp = app.get('/caluire-axel/test/regie/MAREGIE/invoices/history?NameID=yyy') |
|
1343 |
assert resp.json['err'] == 0 |
|
1344 |
assert resp.json['data'] == [ |
|
1345 |
{ |
|
1346 |
'id': 'historical-XXX-42', |
|
1347 |
'display_id': '42', |
|
1348 |
'label': 'PRESTATIONS PERISCOLAIRES SEPTEMBRE-OCTOBRE 2019', |
|
1349 |
'amount': 0, |
|
1350 |
'total_amount': '44.94', |
|
1351 |
'online_payment': False, |
|
1352 |
'pay_limit_date': '', |
|
1353 |
'has_pdf': True, |
|
1354 |
'paid': False, |
|
1355 |
'vendor': { |
|
1356 |
'caluire-axel': { |
|
1357 |
'IDFACTURE': 42, |
|
1358 |
'MONTANT': '44.94', |
|
1359 |
'ENCAISSE': '40.00', |
|
1360 |
'FACTURATION': 'PRESTATIONS PERISCOLAIRES SEPTEMBRE-OCTOBRE 2019', |
|
1361 |
'ECHEANCE': '2019-12-04', |
|
1362 |
'EMISSION': '2019-11-12', |
|
1363 |
'EXISTEPDF': True, |
|
1364 |
} |
|
1365 |
}, |
|
1366 |
}, |
|
1367 |
{ |
|
1368 |
'id': 'historical-XXX-43', |
|
1369 |
'display_id': '43', |
|
1370 |
'label': 'PRESTATIONS PERISCOLAIRES NOVEMBRE 2019', |
|
1371 |
'amount': 0, |
|
1372 |
'total_amount': '44.94', |
|
1373 |
'online_payment': False, |
|
1374 |
'pay_limit_date': '', |
|
1375 |
'has_pdf': False, |
|
1376 |
'paid': False, |
|
1377 |
'vendor': { |
|
1378 |
'caluire-axel': { |
|
1379 |
'IDFACTURE': 43, |
|
1380 |
'FACTURATION': 'PRESTATIONS PERISCOLAIRES NOVEMBRE 2019', |
|
1381 |
'MONTANT': '44.94', |
|
1382 |
'ENCAISSE': '0.00', |
|
1383 |
'ECHEANCE': '2020-01-04', |
|
1384 |
'EMISSION': '2019-12-12', |
|
1385 |
'EXISTEPDF': False, |
|
1386 |
} |
|
1387 |
}, |
|
1388 |
}, |
|
1389 |
] |
|
1278 |
- |