From 7bce7e429c8b967471c94087d72ac0a30dc96e77 Mon Sep 17 00:00:00 2001
From: Nicolas ROCHE <nroche@entrouvert.com>
Date: Tue, 11 May 2021 17:20:26 +0200
Subject: [PATCH 3/4] caluire-axel: add invoice endpoint (#53884)

---
 passerelle/contrib/caluire_axel/models.py |  42 ++++++
 tests/test_caluire_axel.py                | 151 ++++++++++++++++++++++
 2 files changed, 193 insertions(+)

diff --git a/passerelle/contrib/caluire_axel/models.py b/passerelle/contrib/caluire_axel/models.py
index 91df64e0..6ff24447 100644
--- a/passerelle/contrib/caluire_axel/models.py
+++ b/passerelle/contrib/caluire_axel/models.py
@@ -601,16 +601,27 @@ class CaluireAxel(BaseResource):
             )
 
         data = result.json_response['DATA']['PORTAIL']['GETLISTFACTURES']
         result = []
         for facture in data.get('FACTURE', []):
             result.append(utils.normalize_invoice(facture, family_id, historical=True))
         return result
 
+    def get_invoice(self, regie_id, invoice_id, family_id, historical=None, nb_mounts_limit=None):
+        if historical:
+            invoices_data = self.get_historical_invoices(
+                regie_id=regie_id, family_id=family_id, nb_mounts_limit=nb_mounts_limit
+            )
+        else:
+            invoices_data = self.get_invoices(regie_id=regie_id, family_id=family_id)
+        for invoice in invoices_data:
+            if invoice['display_id'] == invoice_id:
+                return invoice
+
     @endpoint(
         display_category=_('Invoices'),
         display_order=1,
         name='regie',
         perm='can_access',
         pattern=r'^(?P<regie_id>[\w-]+)/invoices/?$',
         example_pattern='{regie_id}/invoices',
         description=_("Get invoices to pay"),
@@ -640,16 +651,47 @@ class CaluireAxel(BaseResource):
     )
     def invoices_history(self, request, regie_id, NameID, nb_mounts_limit='3'):
         link = self.get_link(NameID)
         invoices_data = self.get_historical_invoices(
             regie_id, family_id=link.family_id, nb_mounts_limit=nb_mounts_limit
         )
         return {'data': invoices_data}
 
+    @endpoint(
+        display_category=_('Invoices'),
+        display_order=3,
+        name='regie',
+        perm='can_access',
+        pattern=r'^(?P<regie_id>[\w-]+)/invoice/(?P<invoice_id>(historical-)?\w+-\d+)/?$',
+        example_pattern='{regie_id}/invoice/{invoice_id}',
+        description=_('Get invoice details'),
+        parameters={
+            'NameID': {'description': _('Publik ID')},
+            'regie_id': {'description': _('Regie identifier'), 'example_value': 'ENF'},
+            'invoice_id': {'description': _('Invoice identifier'), 'example_value': 'IDFAM-42'},
+            'nb_mounts_limit': {'description': _('Number of months of history'), 'example_value': '12'},
+        },
+    )
+    def invoice(self, request, regie_id, invoice_id, NameID, nb_mounts_limit='12'):
+        link = self.get_link(NameID)
+        real_invoice_id = invoice_id.split('-')[-1]
+        historical = invoice_id.startswith('historical-')
+        invoice = self.get_invoice(
+            regie_id=regie_id,
+            invoice_id=real_invoice_id,
+            family_id=link.family_id,
+            historical=historical,
+            nb_mounts_limit=nb_mounts_limit,
+        )
+        if invoice is None:
+            raise APIError('Invoice not found', err_code='not-found')
+
+        return {'data': invoice}
+
 
 class Link(models.Model):
     resource = models.ForeignKey(CaluireAxel, on_delete=models.CASCADE)
     name_id = models.CharField(blank=False, max_length=256)
     family_id = models.CharField(blank=False, max_length=128)
     person_id = models.CharField(blank=False, max_length=128)
 
     class Meta:
diff --git a/tests/test_caluire_axel.py b/tests/test_caluire_axel.py
index 332a3941..bb181120 100644
--- a/tests/test_caluire_axel.py
+++ b/tests/test_caluire_axel.py
@@ -1382,8 +1382,159 @@ def test_invoices_history_endpoint(app, resource):
                     'ENCAISSE': '0.00',
                     'ECHEANCE': '2020-01-04',
                     'EMISSION': '2019-12-12',
                     'EXISTEPDF': False,
                 }
             },
         },
     ]
+
+
+def test_invoice_endpoint_axel_error(app, resource):
+    Link.objects.create(resource=resource, name_id='yyy', family_id='XXX', person_id='42')
+    with mock.patch('passerelle.contrib.caluire_axel.schemas.get_factures_a_payer') as operation:
+        operation.side_effect = AxelError('FooBar')
+        resp = app.get('/caluire-axel/test/regie/MAREGIE/invoice/XXX-42?NameID=yyy')
+    assert resp.json['err_desc'] == "Axel error: FooBar"
+    assert resp.json['err'] == 'error'
+
+
+def test_invoice_endpoint_bad_request(app, resource):
+    Link.objects.create(resource=resource, name_id='yyy', family_id='XXX', person_id='42')
+    with mock_data(None, 'GetListFactures'):
+        resp = app.get(
+            '/caluire-axel/test/regie/MAREGIE/invoice/historical-XXX-42?NameID=yyy&nb_mounts_limit=not_a_number',
+            status=400,
+        )
+    assert resp.json['err_desc'] == "nb_mounts_limit must be an integer"
+    assert resp.json['err'] == 'bad-request'
+
+    content = '''<PORTAIL>
+    <GETFACTURESAPAYER>
+        <CODE>-3</CODE>
+    </GETFACTURESAPAYER>
+</PORTAIL>'''
+    with mock_data(content, 'GetFacturesaPayer'):
+        resp = app.get('/caluire-axel/test/regie/MAREGIE/invoice/XXX-42?NameID=yyy')
+    assert resp.json['err_desc'] == "Wrong get-invoices status"
+    assert resp.json['err'] == 'get-invoices-code-error--3'
+
+    content = '''<PORTAIL>
+    <GETLISTFACTURES>
+        <CODE>-3</CODE>
+    </GETLISTFACTURES>
+</PORTAIL>'''
+    with mock_data(content, 'GetListFactures'):
+        resp = app.get('/caluire-axel/test/regie/MAREGIE/invoice/historical-XXX-42?NameID=yyy')
+    assert resp.json['err_desc'] == "Wrong get-historical-invoices status"
+    assert resp.json['err'] == 'get-historical-invoices-code-error--3'
+
+
+def test_invoice_endpoint_no_result(app, resource):
+    resp = app.get('/caluire-axel/test/regie/MAREGIE/invoice/XXX-42?NameID=yyy')
+    assert resp.json['err_desc'] == "Person not found"
+    assert resp.json['err'] == 'not-found'
+
+    Link.objects.create(resource=resource, name_id='yyy', family_id='XXX', person_id='42')
+    filepath = os.path.join(os.path.dirname(__file__), 'data/caluire_axel/invoices.xml')
+    with open(filepath) as xml:
+        invoices = xml.read()
+    content = (
+        '''<PORTAIL>
+  <GETFACTURESAPAYER>
+    %s
+  </GETFACTURESAPAYER>
+</PORTAIL>'''
+        % invoices
+    )
+    with mock_data(content, 'GetFacturesaPayer'):
+        resp = app.get('/caluire-axel/test/regie/MAREGIE/invoice/XXX-35?NameID=yyy')
+    assert resp.json['err_desc'] == "Invoice not found"
+    assert resp.json['err'] == 'not-found'
+    content = (
+        '''<PORTAIL>
+    <GETLISTFACTURES>
+        %s
+    </GETLISTFACTURES>
+</PORTAIL>'''
+        % invoices
+    )
+    with mock_data(content, 'GetListFactures'):
+        resp = app.get('/caluire-axel/test/regie/MAREGIE/invoice/historical-XXX-44?NameID=yyy')
+    assert resp.json['err_desc'] == "Invoice not found"
+    assert resp.json['err'] == 'not-found'
+
+
+def test_invoice_endpoint(app, resource):
+    Link.objects.create(resource=resource, name_id='yyy', family_id='XXX', person_id='42')
+    filepath = os.path.join(os.path.dirname(__file__), 'data/caluire_axel/invoices.xml')
+    with open(filepath) as xml:
+        invoices = xml.read()
+    content = (
+        '''<PORTAIL>
+  <GETFACTURESAPAYER>
+    %s
+  </GETFACTURESAPAYER>
+</PORTAIL>'''
+        % invoices
+    )
+    with mock_data(content, 'GetFacturesaPayer'):
+        resp = app.get('/caluire-axel/test/regie/MAREGIE/invoice/XXX-42?NameID=yyy')
+    assert resp.json['err'] == 0
+    assert resp.json['data'] == {
+        'id': 'XXX-42',
+        'display_id': '42',
+        'label': 'PRESTATIONS PERISCOLAIRES SEPTEMBRE-OCTOBRE 2019',
+        'amount': '4.94',
+        'total_amount': '44.94',
+        'amount_paid': '40.00',
+        'online_payment': True,
+        'created': '2019-11-12',
+        'pay_limit_date': '2019-12-04',
+        'has_pdf': True,
+        'paid': False,
+        'vendor': {
+            'caluire-axel': {
+                'IDFACTURE': 42,
+                'MONTANT': '44.94',
+                'ENCAISSE': '40.00',
+                'FACTURATION': 'PRESTATIONS PERISCOLAIRES SEPTEMBRE-OCTOBRE 2019',
+                'ECHEANCE': '2019-12-04',
+                'EMISSION': '2019-11-12',
+                'EXISTEPDF': True,
+            }
+        },
+    }
+
+    content = (
+        '''<PORTAIL>
+    <GETLISTFACTURES>
+        %s
+    </GETLISTFACTURES>
+</PORTAIL>'''
+        % invoices
+    )
+    with mock_data(content, 'GetListFactures'):
+        resp = app.get('/caluire-axel/test/regie/MAREGIE/invoice/historical-XXX-42?NameID=yyy')
+    assert resp.json['err'] == 0
+    assert resp.json['data'] == {
+        'id': 'historical-XXX-42',
+        'display_id': '42',
+        'label': 'PRESTATIONS PERISCOLAIRES SEPTEMBRE-OCTOBRE 2019',
+        'amount': 0,
+        'total_amount': '44.94',
+        'online_payment': False,
+        'pay_limit_date': '',
+        'has_pdf': True,
+        'paid': False,
+        'vendor': {
+            'caluire-axel': {
+                'IDFACTURE': 42,
+                'MONTANT': '44.94',
+                'ENCAISSE': '40.00',
+                'FACTURATION': 'PRESTATIONS PERISCOLAIRES SEPTEMBRE-OCTOBRE 2019',
+                'ECHEANCE': '2019-12-04',
+                'EMISSION': '2019-11-12',
+                'EXISTEPDF': True,
+            },
+        },
+    }
-- 
2.30.2

