Projet

Général

Profil

0002-caluire-axel-add-invoices_history-endpoint-53884.patch

Nicolas Roche, 02 juin 2021 17:31

Télécharger (13,7 ko)

Voir les différences:

Subject: [PATCH 2/4] caluire-axel: add invoices_history endpoint (#53884)

 passerelle/contrib/caluire_axel/models.py     |  59 +++++++++
 passerelle/contrib/caluire_axel/schemas.py    |   1 +
 .../caluire_axel/xsd/Q_GetListFactures.xsd    |  28 +++++
 .../caluire_axel/xsd/R_GetListFactures.xsd    |  55 +++++++++
 tests/test_caluire_axel.py                    | 112 ++++++++++++++++++
 5 files changed, 255 insertions(+)
 create mode 100644 passerelle/contrib/caluire_axel/xsd/Q_GetListFactures.xsd
 create mode 100644 passerelle/contrib/caluire_axel/xsd/R_GetListFactures.xsd
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
-