From aa9585071af7f2f9475559aa9f1c1f6772b70334 Mon Sep 17 00:00:00 2001 From: Nicolas ROCHE Date: Thu, 9 Nov 2023 15:55:23 +0100 Subject: [PATCH] toulouse-maelis: manage online_payment without cache (#83257) --- passerelle/contrib/toulouse_maelis/models.py | 56 ++++++--- .../R_read_invoices_canceled.xml | 106 ++++++++++++++++++ tests/test_toulouse_maelis.py | 64 +++++++++++ 3 files changed, 211 insertions(+), 15 deletions(-) create mode 100644 tests/data/toulouse_maelis/R_read_invoices_canceled.xml diff --git a/passerelle/contrib/toulouse_maelis/models.py b/passerelle/contrib/toulouse_maelis/models.py index b6665b2e..092f9319 100644 --- a/passerelle/contrib/toulouse_maelis/models.py +++ b/passerelle/contrib/toulouse_maelis/models.py @@ -4046,6 +4046,7 @@ class ToulouseMaelis(BaseResource, HTTPResource): def get_invoices(self, family_id, regie_id): self.assert_key_in_referential('Regie', regie_id, 'regie_id parameter') + known_invoice_ids = set() try: result = self.call( 'Invoice', @@ -4056,12 +4057,14 @@ class ToulouseMaelis(BaseResource, HTTPResource): dateEnd=now().strftime(utils.json_date_format), ) except SOAPServiceUnreachable: - pass + known_invoice_ids = None else: for item in result: + invoice_id = item['numInvoice'] + known_invoice_ids.add(invoice_id) invoice, created = self.invoice_set.get_or_create( regie_id=regie_id, - invoice_id=item['numInvoice'], + invoice_id=invoice_id, defaults={ 'family_id': family_id, 'maelis_data': item, @@ -4091,7 +4094,18 @@ class ToulouseMaelis(BaseResource, HTTPResource): "Mise à jour de %s sur la famille '%s': %s", repr(invoice), family_id, diff ) - return self.invoice_set.filter(regie_id=regie_id, family_id=family_id) + qs = self.invoice_set.filter(regie_id=regie_id, family_id=family_id) + qs.known_invoice_ids = known_invoice_ids + return qs + + def make_no_online_payment_reason(self, qs, invoice): + if qs.known_invoice_ids is None: + # soap service is down + return 'Le service est temporairement indisponible.' + elif invoice.invoice_id not in qs.known_invoice_ids: + return 'La facture a été annulée.' + else: + return None @endpoint( display_category='Facture', @@ -4107,12 +4121,14 @@ class ToulouseMaelis(BaseResource, HTTPResource): ) def invoices(self, request, regie_id, NameID=None, family_id=None): family_id = family_id or self.get_link(NameID).family_id - invoices = [ - i.format_content() - for i in self.get_invoices(family_id, regie_id) - if i.status() in ['created', 'for_payment'] - ] - return {'has_invoice_for_payment': True, 'data': invoices} + data = [] + qs = self.get_invoices(family_id, regie_id) + for invoice in qs: + if invoice.status() not in ['created', 'for_payment']: + continue + invoice.no_online_payment_reason = self.make_no_online_payment_reason(qs, invoice) + data.append(invoice.format_content()) + return {'has_invoice_for_payment': True, 'data': data} @endpoint( display_category='Facture', @@ -4138,10 +4154,12 @@ class ToulouseMaelis(BaseResource, HTTPResource): def get_invoice(self, regie_id, invoice_id): real_invoice_id = invoice_id.split('-')[-1] family_id = invoice_id[: -(len(real_invoice_id) + 1)] + qs = self.get_invoices(family_id, regie_id) try: - invoice = self.get_invoices(family_id, regie_id).get(invoice_id=real_invoice_id) + invoice = qs.get(invoice_id=real_invoice_id) except Invoice.DoesNotExist: raise APIError('Invoice not found') + invoice.no_online_payment_reason = self.make_no_online_payment_reason(qs, invoice) return invoice @endpoint( @@ -4168,7 +4186,7 @@ class ToulouseMaelis(BaseResource, HTTPResource): if invoice.status() == 'cancelling': raise APIError('Invoice cancelling') return { - 'data': invoice.format_content(), + 'data': invoice.format_content(no_online_payment_reason=invoice.no_online_payment_reason), } @endpoint( @@ -4321,6 +4339,8 @@ class Invoice(models.Model): basket_generation_date = models.DateTimeField(null=True) maelis_cancel_notification_date = models.DateTimeField(null=True) + no_online_payment_reason = None + def __repr__(self): return '' % (self.regie_id, self.invoice_id) @@ -4351,7 +4371,7 @@ class Invoice(models.Model): # new invoice return 'created' - def format_content(self): + def format_content(self, no_online_payment_reason=None): item = self.maelis_data paid = self.status() in ['paid', 'notified'] amount_paid = item['amountInvoice'] if paid else item['amountPaid'] @@ -4365,15 +4385,21 @@ class Invoice(models.Model): 'amount_paid': amount_paid, 'label': item['libelleTTF'], 'has_pdf': bool(item['pdfName']), - 'online_payment': True, + 'online_payment': not bool(self.no_online_payment_reason), 'paid': paid, 'payment_date': None, - 'no_online_payment_reason': None, + 'no_online_payment_reason': self.no_online_payment_reason, 'reference_id': item['numInvoice'], 'maelis_item': item, } if paid or self.status() == 'for_payment': - invoice.update({'pay_limit_date': '', 'online_payment': False}) + invoice.update( + { + 'pay_limit_date': '', + 'online_payment': False, + 'no_online_payment_reason': None, + } + ) if self.status() == 'for_payment': invoice['no_online_payment_reason'] = 'Transation de payement en cours' return invoice diff --git a/tests/data/toulouse_maelis/R_read_invoices_canceled.xml b/tests/data/toulouse_maelis/R_read_invoices_canceled.xml new file mode 100644 index 00000000..e3c9135c --- /dev/null +++ b/tests/data/toulouse_maelis/R_read_invoices_canceled.xml @@ -0,0 +1,106 @@ + + + + + 8 + F10055591232 + CLAE JANVIER 2023 + + 102 + CANTINE / CLAE + + 1312 + SIMPSON MARGE + 952503.6 + 952503.6 + 0 + 2023-02-24T00:00:00+01:00 + 2023-03-24T00:00:00+01:00 + + 261483 + SIMPSON + MARGE + MME + + + 1 + 261485 + A10049327692 + A10049327693 + Calendrier CLAE SOIR 22/23 + SIMPSON BART + 2023-01-02T00:00:00+01:00 + 2023-02-28T00:00:00+01:00 + 11.0 + 22500.0 + 247500 + + + 2 + 261488 + A10049327692 + A10049327693 + Calendrier CLAE SOIR 22/23 + SIMPSON LISA + 2023-01-02T00:00:00+01:00 + 2023-02-28T00:00:00+01:00 + 6.0 + 22500.0 + 135000 + + + 3 + 261485 + A10049327689 + A10049327690 + Calendrier CLAE MATIN 22/23 + SIMPSON BART + 2023-01-02T00:00:00+01:00 + 2023-02-28T00:00:00+01:00 + 6.0 + 30000.0 + 180000 + + + 4 + 261489 + A10049327689 + A10049327690 + Calendrier CLAE MATIN 22/23 + SIMPSON MAGGIE + 2023-01-02T00:00:00+01:00 + 2023-02-28T00:00:00+01:00 + 8.0 + 30000.0 + 240000 + + + 5 + 261490 + A10049327689 + A10049327690 + Calendrier CLAE MATIN 22/23 + SIMPSON HUGO + 2023-01-02T00:00:00+01:00 + 2023-02-28T00:00:00+01:00 + 5.0 + 30000.0 + 150000 + + + 6 + 261485 + A10049327686 + A10049327687 + Calendrier CLAE MIDI 22/23 + SIMPSON BART + 2023-01-02T00:00:00+01:00 + 2023-02-28T00:00:00+01:00 + 6.0 + 0.6 + 3.6 + + + + + diff --git a/tests/test_toulouse_maelis.py b/tests/test_toulouse_maelis.py index 56ee37cd..af34c88b 100644 --- a/tests/test_toulouse_maelis.py +++ b/tests/test_toulouse_maelis.py @@ -10659,6 +10659,70 @@ def test_invoice(invoice_service, con, app): assert resp.json['data']['label'] == 'CLAE JANVIER 2023' +@mock.patch('passerelle.utils.Request.get') +@mock.patch('passerelle.utils.Request.post') +def test_invoice_online_payment_no_response(mocked_post, mocked_get, con, app, db): + mocked_get.side_effect = (INVOICE_SERVICE_WSDL,) + mocked_post.side_effect = [ + FakedResponse(content=get_xml_file('R_read_invoices.xml'), status_code=200), + ReadTimeout('timeout'), + ReadTimeout('timeout'), + ] + + url = get_endpoint('regie/102/invoice/1312-30') + + # Assert we get the invoice in cache + resp = app.get(url + '?NameID=ignored') + assert resp.json['err'] == 0 + assert resp.json['data']['online_payment'] is True + assert resp.json['data']['no_online_payment_reason'] is None + + # Maelis is no more available + resp = app.get(url + '?NameID=ignored') + assert resp.json['err'] == 0 + assert resp.json['data']['online_payment'] is False + assert resp.json['data']['no_online_payment_reason'] == 'Le service est temporairement indisponible.' + + # No change on invoice already paid + url = get_endpoint('regie/102/invoice/1312-8') + resp = app.get(url + '?NameID=ignored') + assert resp.json['err'] == 0 + assert resp.json['data']['online_payment'] is False + assert resp.json['data']['no_online_payment_reason'] is None + + +def test_invoices_online_payment_no_invoice(invoice_service, con, app, caplog, freezer): + invoice_service.add_soap_response('readInvoices', get_xml_file('R_read_invoices.xml')) + invoice_service.add_soap_response('readInvoices', get_xml_file('R_read_invoices_canceled.xml')) + invoice_service.add_soap_response('readInvoices', get_xml_file('R_read_invoices.xml')) + + url = get_endpoint('regie/102/invoices') + '?NameID=local' + Link.objects.create(resource=con, family_id='1312', name_id='local') + + freezer.move_to('2023-03-03 18:00:00') + resp = app.get(url) + assert resp.json['err'] == 0 + assert len(resp.json['data']) == 1 + assert resp.json['data'][0]['online_payment'] is True + assert resp.json['data'][0]['no_online_payment_reason'] is None + + # Maelis do not send the "canceled by agents" invoice + freezer.move_to('2023-03-03 18:10:00') + resp = app.get(url) + assert resp.json['err'] == 0 + assert len(resp.json['data']) == 1 + assert resp.json['data'][0]['online_payment'] is False + assert resp.json['data'][0]['no_online_payment_reason'] == 'La facture a été annulée.' + + # Maelis re-send the invoice + freezer.move_to('2023-03-03 18:20:00') + resp = app.get(url) + assert resp.json['err'] == 0 + assert len(resp.json['data']) == 1 + assert resp.json['data'][0]['online_payment'] is True + assert resp.json['data'][0]['no_online_payment_reason'] is None + + def test_invoice_if_cancelled(activity_service, invoice_service, con, app, freezer): activity_service.add_soap_response('getFamilyBasket', get_xml_file('R_get_family_basket.xml')) activity_service.add_soap_response('validateBasket', get_xml_file('R_validate_basket.xml'))