Projet

Général

Profil

0003-lingo-make-BasketItem-creation-idempotent-for-paid-i.patch

Benjamin Dauvergne, 30 juillet 2021 11:35

Télécharger (4,23 ko)

Voir les différences:

Subject: [PATCH 3/3] lingo: make BasketItem creation idempotent for paid
 invoices

Using get_or_create() based on the remote_item_id field inside a
transaction is idempotent, the @self.items.add(...)@ operation also (if
the item is already linked it does nothing).

The new behaviour garantee that event if we cannot notify the regie's
web-service, a BasketItem is created to show the user its payment has
been recorded.
 combo/apps/lingo/models.py  | 23 ++++++++++++++---------
 tests/test_lingo_payment.py |  6 ++++++
 2 files changed, 20 insertions(+), 9 deletions(-)
combo/apps/lingo/models.py
857 857
        for item_id in items.split(','):
858 858
            try:
859 859
                remote_item = regie.get_invoice(user=self.user, invoice_id=item_id, raise_4xx=True)
860
                with atomic(savepoint=False):
861
                    self.items.add(self.create_paid_invoice_basket_item(item_id, remote_item))
860 862
                regie.pay_invoice(item_id, self.order_id, self.bank_transaction_date or self.end_date)
861 863
            except ObjectDoesNotExist:
862 864
                # 4xx error
......
879 881
                )
880 882
            else:
881 883
                logger.info('notified payment for remote item %s from transaction %s', item_id, self)
882
                self.items.add(self.create_paid_invoice_basket_item(remote_item))
883 884

  
884 885
        self.to_be_paid_remote_items = ','.join(to_be_paid_remote_items) or None
885 886
        self.save(update_fields=['to_be_paid_remote_items'])
886 887

  
887
    def create_paid_invoice_basket_item(self, remote_item):
888
    def create_paid_invoice_basket_item(self, item_id, remote_item):
888 889
        subject = _('Invoice #%s') % remote_item.display_id
889
        return BasketItem.objects.create(
890
            user=self.user,
891
            regie=remote_item.regie,
892
            source_url='',
893
            subject=subject,
894
            amount=remote_item.amount,
895
            payment_date=self.end_date,
890
        basket_item, created = BasketItem.objects.get_or_create(
891
            remote_item_id=item_id,
892
            defaults=dict(
893
                user=self.user,
894
                regie=remote_item.regie,
895
                source_url='',
896
                subject=subject,
897
                amount=remote_item.amount,
898
                payment_date=self.end_date,
899
            ),
896 900
        )
901
        return basket_item
897 902

  
898 903
    def handle_backend_response(self, response, callback=True):
899 904
        logger.debug('lingo: regie "%s" handling response for transaction "%%s"' % self.regie, self.order_id)
tests/test_lingo_payment.py
1120 1120
    mock_err = mock.Mock(status_code=200)
1121 1121
    mock_err.json.return_url = {'err': 1}
1122 1122

  
1123
    assert transaction.items.count() == 0
1124

  
1123 1125
    # error on get invoice
1124 1126
    mock_request.side_effect = [
1125 1127
        ConnectionError('where is my hostname?'),  # get invoice 42
......
1128 1130
    ]
1129 1131
    appconfig.update_transactions()
1130 1132
    transaction.refresh_from_db()
1133
    assert transaction.items.count() == 1  # only 35 was found
1134
    assert set(transaction.items.values_list('remote_item_id', flat=True)) == {'35'}
1131 1135
    assert transaction.to_be_paid_remote_items == '42'  # retry for first one
1132 1136

  
1133 1137
    # error on pay invoice
......
1140 1144
    ]
1141 1145
    appconfig.update_transactions()
1142 1146
    transaction.refresh_from_db()
1147
    assert transaction.items.count() == 2  # both were updated now that get_invoice worked for 42
1148
    assert set(transaction.items.values_list('remote_item_id', flat=True)) == {'35', '42'}
1143 1149
    assert transaction.to_be_paid_remote_items == '42'  # retry for first one
1144 1150

  
1145 1151
    # unknown error on get invoice
1146
-