0002-caluire-axel-add-invoices_history-endpoint-53884.patch
passerelle/contrib/caluire_axel/models.py | ||
---|---|---|
323 | 323 | |
324 | 324 |
data = result.json_response['DATA']['PORTAIL']['GETFACTURESAPAYER'] |
325 | 325 |
result = [] |
326 | 326 | |
327 | 327 |
for facture in data.get('FACTURE', []): |
328 | 328 |
result.append(utils.normalize_invoice(facture, link.family_id)) |
329 | 329 |
return result |
330 | 330 | |
331 |
def get_historical_invoices(self, regie_id, name_id, nb_mounts_limit): |
|
332 |
link = self.get_link(name_id) |
|
333 |
try: |
|
334 |
nb_mois = int(nb_mounts_limit) |
|
335 |
except ValueError: |
|
336 |
raise APIError('nb_mounts_limit must be an integer', err_code='bad-request', http_status=400) |
|
337 |
try: |
|
338 |
result = schemas.get_list_factures( |
|
339 |
self, |
|
340 |
{ |
|
341 |
'PORTAIL': { |
|
342 |
'GETLISTFACTURES': { |
|
343 |
'IDENTFAMILLE': link.family_id, |
|
344 |
'IDENTREGIEFACT': regie_id, |
|
345 |
'NBMOIS': nb_mois, |
|
346 |
} |
|
347 |
}, |
|
348 |
}, |
|
349 |
) |
|
350 |
except axel.AxelError as e: |
|
351 |
raise APIError( |
|
352 |
'Axel error: %s' % e, |
|
353 |
err_code='error', |
|
354 |
data={'xml_request': e.xml_request, 'xml_response': e.xml_response}, |
|
355 |
) |
|
356 | ||
357 |
data = result.json_response['DATA']['PORTAIL']['GETLISTFACTURES'] |
|
358 |
result = [] |
|
359 |
for facture in data.get('FACTURE', []): |
|
360 |
result.append(utils.normalize_invoice(facture, link.family_id, historical=True)) |
|
361 |
return result |
|
362 | ||
331 | 363 |
@endpoint( |
332 | 364 |
display_category=_('Invoices'), |
333 | 365 |
display_order=1, |
334 | 366 |
name='regie', |
335 | 367 |
perm='can_access', |
336 | 368 |
pattern=r'^(?P<regie_id>[\w-]+)/invoices/?$', |
337 | 369 |
example_pattern='{regie_id}/invoices', |
338 | 370 |
description=_("Get invoices to pay"), |
... | ... | |
340 | 372 |
'NameID': {'description': _('Publik ID')}, |
341 | 373 |
'regie_id': {'description': _('Regie identifier'), 'example_value': 'ENF'}, |
342 | 374 |
}, |
343 | 375 |
) |
344 | 376 |
def invoices(self, request, regie_id, NameID): |
345 | 377 |
invoices_data = self.get_invoices(regie_id=regie_id, name_id=NameID) |
346 | 378 |
return {'data': invoices_data} |
347 | 379 | |
380 |
@endpoint( |
|
381 |
display_category=_('Invoices'), |
|
382 |
display_order=2, |
|
383 |
name='regie', |
|
384 |
perm='can_access', |
|
385 |
pattern=r'^(?P<regie_id>[\w-]+)/invoices/history/?$', |
|
386 |
example_pattern='{regie_id}/invoices/history', |
|
387 |
description=_("Get invoices already paid"), |
|
388 |
parameters={ |
|
389 |
'NameID': {'description': _('Publik ID')}, |
|
390 |
'regie_id': {'description': _('Regie identifier'), 'example_value': 'ENF'}, |
|
391 |
'nb_mounts_limit': {'description': _('Number of months of history'), 'example_value': '3'}, |
|
392 |
}, |
|
393 |
) |
|
394 |
def invoices_history(self, request, regie_id, NameID, nb_mounts_limit='3'): |
|
395 |
invoices_data = self.get_historical_invoices( |
|
396 |
regie_id, name_id=NameID, nb_mounts_limit=nb_mounts_limit |
|
397 |
) |
|
398 |
return {'data': invoices_data} |
|
399 | ||
348 | 400 | |
349 | 401 |
class Link(models.Model): |
350 | 402 |
resource = models.ForeignKey(CaluireAxel, on_delete=models.CASCADE) |
351 | 403 |
name_id = models.CharField(blank=False, max_length=256) |
352 | 404 |
family_id = models.CharField(blank=False, max_length=128) |
353 | 405 |
person_id = models.CharField(blank=False, max_length=128) |
354 | 406 | |
355 | 407 |
class Meta: |
passerelle/contrib/caluire_axel/schemas.py | ||
---|---|---|
72 | 72 |
axel_schema = CaluireAxelSchema |
73 | 73 | |
74 | 74 | |
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_factures_a_payer = Operation('GetFacturesaPayer') |
80 |
get_list_factures = Operation('GetListFactures') |
|
80 | 81 | |
81 | 82 | |
82 | 83 |
LINK_SCHEMA = copy.deepcopy( |
83 | 84 |
find_individus.request_schema['properties']['PORTAIL']['properties']['FINDINDIVIDU'] |
84 | 85 |
) |
85 | 86 |
for key in ['NAISSANCE', 'CODEPOSTAL', 'VILLE', 'TEL', 'MAIL']: |
86 | 87 |
LINK_SCHEMA['properties'].pop(key) |
87 | 88 |
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="DATEEMISSION"/> |
|
31 |
<xsd:element ref="DATEECHEANCE"/> |
|
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="DATEEMISSION" type="all:DATEType"/> |
|
49 |
<xsd:element name="DATEECHEANCE" type="all:DATEType"/> |
|
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 | ||
---|---|---|
787 | 787 |
'ENCAISSE': '0.00', |
788 | 788 |
'DATEECHEANCE': '2020-01-04', |
789 | 789 |
'DATEEMISSION': '2019-12-12', |
790 | 790 |
'EXISTEPDF': False, |
791 | 791 |
} |
792 | 792 |
}, |
793 | 793 |
}, |
794 | 794 |
] |
795 | ||
796 | ||
797 |
def test_invoices_history_endpoint_axel_error(app, resource): |
|
798 |
Link.objects.create(resource=resource, name_id='yyy', family_id='XXX', person_id='42') |
|
799 |
with mock.patch('passerelle.contrib.caluire_axel.schemas.get_list_factures') as operation: |
|
800 |
operation.side_effect = AxelError('FooBar') |
|
801 |
resp = app.get('/caluire-axel/test/regie/MAREGIE/invoices/history?NameID=yyy') |
|
802 |
assert resp.json['err_desc'] == "Axel error: FooBar" |
|
803 |
assert resp.json['err'] == 'error' |
|
804 | ||
805 | ||
806 |
def test_invoices_history_endpoint_bad_request(app, resource): |
|
807 |
Link.objects.create(resource=resource, name_id='yyy', family_id='XXX', person_id='42') |
|
808 |
with mock_getdata(None, 'GetListFactures'): |
|
809 |
resp = app.get( |
|
810 |
'/caluire-axel/test/regie/MAREGIE/invoices/history?NameID=yyy&nb_mounts_limit=not_a_number', |
|
811 |
status=400, |
|
812 |
) |
|
813 |
assert resp.json['err_desc'] == "nb_mounts_limit must be an integer" |
|
814 |
assert resp.json['err'] == 'bad-request' |
|
815 | ||
816 | ||
817 |
def test_invoices_history_endpoint_no_result(app, resource): |
|
818 |
resp = app.get('/caluire-axel/test/regie/MAREGIE/invoices/history?NameID=yyy') |
|
819 |
assert resp.json['err_desc'] == "Person not found" |
|
820 |
assert resp.json['err'] == 'not-found' |
|
821 | ||
822 | ||
823 |
def test_invoices_history_endpoint_no_invoice(app, resource): |
|
824 |
Link.objects.create(resource=resource, name_id='yyy', family_id='XXX', person_id='42') |
|
825 |
content = '''<PORTAIL> |
|
826 |
<GETLISTFACTURES> |
|
827 |
<CODE>0</CODE> |
|
828 |
</GETLISTFACTURES> |
|
829 |
</PORTAIL>''' |
|
830 |
with mock_getdata(content, 'GetListFactures'): |
|
831 |
resp = app.get('/caluire-axel/test/regie/MAREGIE/invoices/history?NameID=yyy') |
|
832 |
assert resp.json['err'] == 0 |
|
833 |
assert resp.json['data'] == [] |
|
834 | ||
835 | ||
836 |
def test_invoices_history_endpoint(app, resource): |
|
837 |
Link.objects.create(resource=resource, name_id='yyy', family_id='XXX', person_id='42') |
|
838 |
filepath = os.path.join(os.path.dirname(__file__), 'data/caluire_axel/invoices.xml') |
|
839 |
with open(filepath) as xml: |
|
840 |
content = ( |
|
841 |
'''<PORTAIL> |
|
842 |
<GETLISTFACTURES> |
|
843 |
%s |
|
844 |
</GETLISTFACTURES> |
|
845 |
</PORTAIL>''' |
|
846 |
% xml.read() |
|
847 |
) |
|
848 |
with mock_getdata(content, 'GetListFactures'): |
|
849 |
resp = app.get('/caluire-axel/test/regie/MAREGIE/invoices/history?NameID=yyy') |
|
850 |
assert resp.json['err'] == 0 |
|
851 |
assert resp.json['data'] == [ |
|
852 |
{ |
|
853 |
'id': 'historical-XXX-42', |
|
854 |
'display_id': '42', |
|
855 |
'label': 'PRESTATIONS PERISCOLAIRES SEPTEMBRE-OCTOBRE 2019', |
|
856 |
'amount': 0, |
|
857 |
'total_amount': '44.94', |
|
858 |
'online_payment': False, |
|
859 |
'pay_limit_date': '', |
|
860 |
'has_pdf': True, |
|
861 |
'paid': False, |
|
862 |
'vendor': { |
|
863 |
'caluire-axel': { |
|
864 |
'IDFACTURE': 42, |
|
865 |
'MONTANT': '44.94', |
|
866 |
'ENCAISSE': '40.00', |
|
867 |
'FACTURATION': 'PRESTATIONS PERISCOLAIRES SEPTEMBRE-OCTOBRE 2019', |
|
868 |
'DATEECHEANCE': '2019-12-04', |
|
869 |
'DATEEMISSION': '2019-11-12', |
|
870 |
'EXISTEPDF': True, |
|
871 |
} |
|
872 |
}, |
|
873 |
}, |
|
874 |
{ |
|
875 |
'id': 'historical-XXX-43', |
|
876 |
'display_id': '43', |
|
877 |
'label': 'PRESTATIONS PERISCOLAIRES NOVEMBRE 2019', |
|
878 |
'amount': 0, |
|
879 |
'total_amount': '44.94', |
|
880 |
'online_payment': False, |
|
881 |
'pay_limit_date': '', |
|
882 |
'has_pdf': False, |
|
883 |
'paid': False, |
|
884 |
'vendor': { |
|
885 |
'caluire-axel': { |
|
886 |
'IDFACTURE': 43, |
|
887 |
'FACTURATION': 'PRESTATIONS PERISCOLAIRES NOVEMBRE 2019', |
|
888 |
'MONTANT': '44.94', |
|
889 |
'ENCAISSE': '0.00', |
|
890 |
'DATEECHEANCE': '2020-01-04', |
|
891 |
'DATEEMISSION': '2019-12-12', |
|
892 |
'EXISTEPDF': False, |
|
893 |
} |
|
894 |
}, |
|
895 |
}, |
|
896 |
] |
|
795 |
- |