Projet

Général

Profil

0001-caluire-axel-add-invoices-endpoint-53884.patch

Nicolas Roche, 12 mai 2021 12:15

Télécharger (14,8 ko)

Voir les différences:

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

 passerelle/contrib/caluire_axel/models.py     | 47 ++++++++-
 passerelle/contrib/caluire_axel/schemas.py    |  1 +
 passerelle/contrib/caluire_axel/utils.py      | 36 +++++++
 .../caluire_axel/xsd/Q_GetFacturesaPayer.xsd  | 26 +++++
 .../caluire_axel/xsd/R_GetFacturesaPayer.xsd  | 55 +++++++++++
 tests/data/caluire_axel/invoices.xml          | 19 ++++
 tests/test_caluire_axel.py                    | 96 +++++++++++++++++++
 7 files changed, 279 insertions(+), 1 deletion(-)
 create mode 100644 passerelle/contrib/caluire_axel/xsd/Q_GetFacturesaPayer.xsd
 create mode 100644 passerelle/contrib/caluire_axel/xsd/R_GetFacturesaPayer.xsd
 create mode 100644 tests/data/caluire_axel/invoices.xml
passerelle/contrib/caluire_axel/models.py
30 30

  
31 31
class CaluireAxel(BaseResource):
32 32

  
33 33
    wsdl_url = models.CharField(
34 34
        max_length=128, blank=False, verbose_name=_('WSDL URL'), help_text=_('Caluire Axel WSDL URL')
35 35
    )
36 36

  
37 37
    category = _('Business Process Connectors')
38
    _category_ordering = [_('Family account'), _('Schooling')]
38
    _category_ordering = [_('Family account'), _('Schooling'), _('Invoices')]
39 39

  
40 40
    class Meta:
41 41
        verbose_name = _('Caluire Axel')
42 42

  
43 43
    def check_status(self):
44 44
        response = self.requests.get(self.wsdl_url)
45 45
        response.raise_for_status()
46 46

  
......
295 295
                err_code='error',
296 296
                data={'xml_request': e.xml_request, 'xml_response': e.xml_response},
297 297
            )
298 298

  
299 299
        schooling_data = result.json_response['DATA']['PORTAIL']['GETINDIVIDU']
300 300

  
301 301
        return {'data': schooling_data}
302 302

  
303
    def get_invoices(self, regie_id, name_id):
304
        link = self.get_link(name_id)
305
        try:
306
            result = schemas.get_factures_a_payer(
307
                self,
308
                {
309
                    'PORTAIL': {
310
                        'GETFACTURESAPAYER': {
311
                            'IDENTFAMILLE': link.family_id,
312
                            'IDENTREGIEFACT': regie_id,
313
                        }
314
                    }
315
                },
316
            )
317
        except axel.AxelError as e:
318
            raise APIError(
319
                'Axel error: %s' % e,
320
                err_code='error',
321
                data={'xml_request': e.xml_request, 'xml_response': e.xml_response},
322
            )
323

  
324
        data = result.json_response['DATA']['PORTAIL']['GETFACTURESAPAYER']
325
        result = []
326

  
327
        for facture in data.get('FACTURE', []):
328
            result.append(utils.normalize_invoice(facture, link.family_id))
329
        return result
330

  
331
    @endpoint(
332
        display_category=_('Invoices'),
333
        display_order=1,
334
        name='regie',
335
        perm='can_access',
336
        pattern=r'^(?P<regie_id>[\w-]+)/invoices/?$',
337
        example_pattern='{regie_id}/invoices',
338
        description=_("Get invoices to pay"),
339
        parameters={
340
            'NameID': {'description': _('Publik ID')},
341
            'regie_id': {'description': _('Regie identifier'), 'example_value': 'ENF'},
342
        },
343
    )
344
    def invoices(self, request, regie_id, NameID):
345
        invoices_data = self.get_invoices(regie_id=regie_id, name_id=NameID)
346
        return {'data': invoices_data}
347

  
303 348

  
304 349
class Link(models.Model):
305 350
    resource = models.ForeignKey(CaluireAxel, on_delete=models.CASCADE)
306 351
    name_id = models.CharField(blank=False, max_length=256)
307 352
    family_id = models.CharField(blank=False, max_length=128)
308 353
    person_id = models.CharField(blank=False, max_length=128)
309 354

  
310 355
    class Meta:
passerelle/contrib/caluire_axel/schemas.py
71 71
    base_xsd_path = BASE_XSD_PATH
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
get_factures_a_payer = Operation('GetFacturesaPayer')
79 80

  
80 81

  
81 82
LINK_SCHEMA = copy.deepcopy(
82 83
    find_individus.request_schema['properties']['PORTAIL']['properties']['FINDINDIVIDU']
83 84
)
84 85
for key in ['NAISSANCE', 'CODEPOSTAL', 'VILLE', 'TEL', 'MAIL']:
85 86
    LINK_SCHEMA['properties'].pop(key)
86 87
    LINK_SCHEMA['required'].remove(key)
passerelle/contrib/caluire_axel/utils.py
16 16
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 17

  
18 18

  
19 19
def get_reference_year_from_date(booking_date):
20 20
    if booking_date.month <= 8:
21 21
        # between january and august, reference year is the year just before
22 22
        return booking_date.year - 1
23 23
    return booking_date.year
24

  
25

  
26
def normalize_invoice(invoice, family_id, historical=False, vendor_base=None):
27
    vendor = vendor_base or {}
28
    vendor.update(invoice)
29
    invoice_id = '%s-%s' % (family_id, invoice['IDFACTURE'])
30
    if historical:
31
        invoice_id = 'historical-%s' % invoice_id
32
    data = {
33
        'id': invoice_id,
34
        'display_id': str(invoice['IDFACTURE']),
35
        'label': invoice['FACTURATION'],
36
        'paid': False,
37
        'total_amount': invoice['MONTANT'],
38
        'has_pdf': invoice['EXISTEPDF'],
39
        'vendor': {'caluire-axel': vendor},
40
    }
41
    if historical:
42
        data.update(
43
            {
44
                'amount': 0,
45
                'pay_limit_date': '',
46
                'online_payment': False,
47
            }
48
        )
49
    else:
50
        data.update(
51
            {
52
                'amount': max(0, invoice['MONTANT'] - invoice['ENCAISSE']),
53
                'amount_paid': invoice['ENCAISSE'],
54
                'created': invoice['DATEEMISSION'],
55
                'pay_limit_date': invoice['DATEECHEANCE'],
56
                'online_payment': bool(invoice['MONTANT'] - invoice['ENCAISSE'] > 0),
57
            }
58
        )
59
    return data
passerelle/contrib/caluire_axel/xsd/Q_GetFacturesaPayer.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="GETFACTURESAPAYER" minOccurs="0" maxOccurs="1"/>
9
		</xsd:sequence>  
10
	</xsd:complexType>
11
	
12
	<xsd:complexType name="GETFACTURESAPAYERType">
13
		<xsd:sequence>
14
			<xsd:element ref="IDENTFAMILLE"/>
15
			<xsd:element ref="IDENTREGIEFACT"/>
16
		</xsd:sequence>  
17
	</xsd:complexType>
18
	
19
	<xsd:element name="IDENTFAMILLE" type="all:IDENTREQUIREDType"/>
20
	<xsd:element name="IDENTREGIEFACT" type="all:IDREQUIREDType"/>
21
	
22
	<xsd:element name="GETFACTURESAPAYER" type="GETFACTURESAPAYERType"/>
23
	
24
	<xsd:element name="PORTAIL" type="PORTAILType"/>	
25
		
26
</xsd:schema>
passerelle/contrib/caluire_axel/xsd/R_GetFacturesaPayer.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="GetFacturesaPayer" />
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="GETFACTURESAPAYER" 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="GETFACTURESAPAYERType">
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="GETFACTURESAPAYER" type="GETFACTURESAPAYERType"/>
54
	
55
</xsd:schema>
tests/data/caluire_axel/invoices.xml
1
<CODE>2</CODE>
2
<FACTURE>
3
  <IDFACTURE>42</IDFACTURE>
4
  <MONTANT>44.94</MONTANT>
5
  <ENCAISSE>40.00</ENCAISSE>
6
  <FACTURATION>PRESTATIONS PERISCOLAIRES SEPTEMBRE-OCTOBRE 2019</FACTURATION>
7
  <DATEEMISSION>12/11/2019</DATEEMISSION>
8
  <DATEECHEANCE>04/12/2019</DATEECHEANCE>
9
  <EXISTEPDF>o</EXISTEPDF>
10
</FACTURE>
11
<FACTURE>
12
  <IDFACTURE>43</IDFACTURE>
13
  <MONTANT>44.94</MONTANT>
14
  <ENCAISSE>0.00</ENCAISSE>
15
  <FACTURATION>PRESTATIONS PERISCOLAIRES NOVEMBRE 2019</FACTURATION>
16
  <DATEEMISSION>12/12/2019</DATEEMISSION>
17
  <DATEECHEANCE>04/01/2020</DATEECHEANCE>
18
  <EXISTEPDF>n</EXISTEPDF>
19
</FACTURE>
tests/test_caluire_axel.py
691 691
            'passerelle.contrib.caluire_axel.models.CaluireAxel.get_family_data',
692 692
            return_value=family_data,
693 693
        ):
694 694
            resp = app.get(
695 695
                '/caluire-axel/test/child_schooling_info?NameID=yyy&idpersonne=50632&schooling_date=2021-05-10'
696 696
            )
697 697
    assert resp.json['err'] == 0
698 698
    assert set(resp.json['data'].keys()) == set(['CODE', 'INDIVIDU', 'SCOLAIRE'])
699

  
700

  
701
def test_invoices_endpoint_axel_error(app, resource):
702
    Link.objects.create(resource=resource, name_id='yyy', family_id='XXX', person_id='42')
703
    with mock.patch('passerelle.contrib.caluire_axel.schemas.get_factures_a_payer') as operation:
704
        operation.side_effect = AxelError('FooBar')
705
        resp = app.get('/caluire-axel/test/regie/MAREGIE/invoices?NameID=yyy')
706
    assert resp.json['err_desc'] == "Axel error: FooBar"
707
    assert resp.json['err'] == 'error'
708

  
709

  
710
def test_invoices_endpoint_no_result(app, resource):
711
    resp = app.get('/caluire-axel/test/regie/MAREGIE/invoices?NameID=yyy')
712
    assert resp.json['err_desc'] == "Person not found"
713
    assert resp.json['err'] == 'not-found'
714

  
715

  
716
def test_invoices_endpoint_no_invoice(app, resource):
717
    Link.objects.create(resource=resource, name_id='yyy', family_id='XXX', person_id='42')
718
    content = '''<PORTAIL>
719
    <GETFACTURESAPAYER>
720
        <CODE>0</CODE>
721
    </GETFACTURESAPAYER>
722
</PORTAIL>'''
723
    with mock_getdata(content, 'GetFacturesaPayer'):
724
        resp = app.get('/caluire-axel/test/regie/MAREGIE/invoices?NameID=yyy')
725
    assert resp.json['err'] == 0
726
    assert resp.json['data'] == []
727

  
728

  
729
def test_invoices_endpoint(app, resource):
730
    Link.objects.create(resource=resource, name_id='yyy', family_id='XXX', person_id='42')
731
    filepath = os.path.join(os.path.dirname(__file__), 'data/caluire_axel/invoices.xml')
732
    with open(filepath) as xml:
733
        content = (
734
            '''
735
<PORTAIL>
736
  <GETFACTURESAPAYER>
737
    %s
738
  </GETFACTURESAPAYER>
739
</PORTAIL>'''
740
            % xml.read()
741
        )
742
    with mock_getdata(content, 'GetFacturesaPayer'):
743
        resp = app.get('/caluire-axel/test/regie/MAREGIE/invoices?NameID=yyy')
744
    assert resp.json['err'] == 0
745
    assert resp.json['data'] == [
746
        {
747
            'id': 'XXX-42',
748
            'display_id': '42',
749
            'label': 'PRESTATIONS PERISCOLAIRES SEPTEMBRE-OCTOBRE 2019',
750
            'amount': '4.94',
751
            'total_amount': '44.94',
752
            'amount_paid': '40.00',
753
            'online_payment': True,
754
            'created': '2019-11-12',
755
            'pay_limit_date': '2019-12-04',
756
            'has_pdf': True,
757
            'paid': False,
758
            'vendor': {
759
                'caluire-axel': {
760
                    'IDFACTURE': 42,
761
                    'MONTANT': '44.94',
762
                    'ENCAISSE': '40.00',
763
                    'FACTURATION': 'PRESTATIONS PERISCOLAIRES SEPTEMBRE-OCTOBRE 2019',
764
                    'DATEECHEANCE': '2019-12-04',
765
                    'DATEEMISSION': '2019-11-12',
766
                    'EXISTEPDF': True,
767
                }
768
            },
769
        },
770
        {
771
            'id': 'XXX-43',
772
            'display_id': '43',
773
            'label': 'PRESTATIONS PERISCOLAIRES NOVEMBRE 2019',
774
            'amount': '44.94',
775
            'total_amount': '44.94',
776
            'amount_paid': '0.00',
777
            'online_payment': True,
778
            'created': '2019-12-12',
779
            'pay_limit_date': '2020-01-04',
780
            'has_pdf': False,
781
            'paid': False,
782
            'vendor': {
783
                'caluire-axel': {
784
                    'IDFACTURE': 43,
785
                    'FACTURATION': 'PRESTATIONS PERISCOLAIRES NOVEMBRE 2019',
786
                    'MONTANT': '44.94',
787
                    'ENCAISSE': '0.00',
788
                    'DATEECHEANCE': '2020-01-04',
789
                    'DATEEMISSION': '2019-12-12',
790
                    'EXISTEPDF': False,
791
                }
792
            },
793
        },
794
    ]
699
-