diff --git a/server/config/settings/test.py b/server/config/settings/test.py
index 3a2d0b3d..5115924b 100644
--- a/server/config/settings/test.py
+++ b/server/config/settings/test.py
@@ -23,6 +23,8 @@ EMAIL_BACKEND = "django.core.mail.backends.locmem.EmailBackend"
WHITENOISE_MANIFEST_STRICT = False
AWS_S3_FILE_OVERWRITE = True
+ABACUS_EXPORT_SFTP_PORT = 34343
+
class DisableMigrations(dict):
def __contains__(self, item):
diff --git a/server/integration_tests/abacus_sftp/test_abacus_sftp.py b/server/integration_tests/abacus_sftp/test_abacus_sftp.py
new file mode 100644
index 00000000..efc4f219
--- /dev/null
+++ b/server/integration_tests/abacus_sftp/test_abacus_sftp.py
@@ -0,0 +1,129 @@
+import os
+import shutil
+import tempfile
+from datetime import datetime
+from io import StringIO
+from subprocess import Popen
+from time import sleep
+
+from django.conf import settings
+from django.test import TransactionTestCase
+
+from vbv_lernwelt.core.admin import User
+from vbv_lernwelt.core.create_default_users import create_default_users
+from vbv_lernwelt.core.model_utils import add_countries
+from vbv_lernwelt.shop.invoice.abacus import abacus_ssh_upload
+from vbv_lernwelt.shop.invoice.abacus_sftp_client import AbacusSftpClient
+from vbv_lernwelt.shop.tests.factories import CheckoutInformationFactory
+
+
+class BaseAbacusSftpServerTestCase(TransactionTestCase):
+ def setUp(self):
+ self.start_sftp_server()
+
+ def tearDown(self):
+ if self.sftp_server:
+ self.sftp_server.kill()
+
+ def start_sftp_server(self):
+ self.tmppath = tempfile.mkdtemp()
+ print(self.tmppath)
+ shutil.rmtree(self.tmppath)
+ os.mkdir(self.tmppath)
+ os.mkdir(self.tmppath + "/debitor")
+ os.mkdir(self.tmppath + "/order")
+ self.sftp_server = Popen(
+ f"sftpserver -p {settings.ABACUS_EXPORT_SFTP_PORT} -l INFO",
+ shell=True,
+ cwd=self.tmppath,
+ )
+ sleep(3)
+
+
+class AbacusSftpServerTestCase(BaseAbacusSftpServerTestCase):
+ def test_canWriteFile_toFakeSftpServer(self):
+ with AbacusSftpClient() as client:
+ files = client.listdir(".")
+ self.assertEqual(["debitor", "order"], files)
+
+ str_file = StringIO()
+ str_file.write("Hello world\n")
+ str_file.seek(0)
+ client.putfo(str_file, "hello.txt")
+
+ files = client.listdir(".")
+ self.assertEqual({"debitor", "order", "hello.txt"}, set(files))
+
+
+class AbacusInvoiceUploadTestCase(BaseAbacusSftpServerTestCase):
+ def setUp(self):
+ super().setUp()
+ add_countries(small_set=True)
+ create_default_users()
+
+ def test_upload_abacus_xml(self):
+ # set abacus_number before
+ _pat = User.objects.get(username="patrizia.huggel@eiger-versicherungen.ch")
+ _pat.abacus_debitor_number = 60000011
+ _pat.save()
+ _ignore_checkout_information = CheckoutInformationFactory(
+ user=_pat, abacus_order_id=6_000_000_123
+ )
+
+ feuz = User.objects.get(username="andreas.feuz@eiger-versicherungen.ch")
+ feuz_checkout_info = CheckoutInformationFactory(
+ user=feuz,
+ transaction_id="24021508331287484",
+ first_name="Andreas",
+ last_name="Feuz",
+ street="Eggersmatt",
+ street_number="32",
+ postal_code="1719",
+ city="Zumholz",
+ country_id="CH",
+ invoice_address="org",
+ organisation_detail_name="VBV",
+ organisation_street="Laupenstrasse",
+ organisation_street_number="10",
+ organisation_postal_code="3000",
+ organisation_city="Bern",
+ organisation_country_id="CH",
+ )
+ feuz_checkout_info.created_at = datetime(2024, 2, 15, 8, 33, 12, 0)
+
+ abacus_ssh_upload(feuz_checkout_info)
+
+ # check if files got created
+ debitor_filepath = self.tmppath + "/debitor/myVBV_debi_60000012.xml"
+ self.assertTrue(os.path.exists(debitor_filepath))
+
+ with open(debitor_filepath) as debitor_file:
+ debi_content = debitor_file.read()
+ assert "
no subscription found
- # """, - # status=404, - # ) - # - # if request.method == "GET": - # if ( - # sub - # and sub.partner_status - # == SubhubCustomerSubscription.PARTNER_STATUS_ENROLLED - # ): - # return HttpResponse( - # content=f""" - # {header} - #already activated
- # """, - # status=200, - # ) - # if request.method == "POST": - # if sub: - # response = requests.post( - # f"{settings.APPLICATION_ABSOLUTE_URL}/subhub/ottwebhook", - # json={ - # "PartnerIntegration": { - # "effectiveDate": datetime.now().isoformat(), - # "eventType": "activation", - # "offerId": sub.subscription_choice.partner_product_id, - # "optionalAttributes": None, - # "pai": pai, - # "partnerType": "Tamedia", - # "transactionId": str(uuid.uuid4()), - # }, - # "eventId": str(uuid.uuid4()), - # "eventType": "OTT Partner Events", - # "publisherId": "Partner Events", - # "status": "new", - # "timestamp": datetime.now().isoformat(), - # }, - # auth=HTTPBasicAuth( - # "swisscom_ott_webhook", - # "swisscom-ott-webhook-rLaYG0btVJMPtfnzfLilZtm50", - # ), - # ) - # print(response) - # return redirect(f"{create_register_url(fake_tamedia_token)}") - # - # if api_url.startswith("/enroll") and request.method == "POST": - # return HttpResponse(status=204) - # - if api_url == "/v1/transactions" and request.method == "POST": data = json.loads(request.body.decode("utf-8")) user = User.objects.get(id=data["user_id"]) @@ -107,10 +35,6 @@ def fake_datatrans_pay_view(request, api_url=""): def call_transaction_complete_webhook( webhook_url, transaction_id, datatrans_status="settled" ): - import time - - import requests - time.sleep(1) payload = { diff --git a/server/vbv_lernwelt/shop/invoice/abacus.py b/server/vbv_lernwelt/shop/invoice/abacus.py index 982488a0..46049502 100644 --- a/server/vbv_lernwelt/shop/invoice/abacus.py +++ b/server/vbv_lernwelt/shop/invoice/abacus.py @@ -4,12 +4,10 @@ from xml.dom import minidom from xml.etree.ElementTree import Element, SubElement, tostring import structlog -from django.conf import settings -from paramiko.client import AutoAddPolicy, SSHClient +from vbv_lernwelt.shop.invoice.abacus_sftp_client import AbacusSftpClient from vbv_lernwelt.shop.models import CheckoutInformation - logger = structlog.get_logger(__name__) @@ -22,7 +20,11 @@ def abacus_ssh_upload(checkout_information: CheckoutInformation): abacus_ssh_upload_invoice( customer_xml_filename, customer_xml_content, folder="debitor" ) - abacus_ssh_upload_invoice(invoice_xml_filename, invoice_xml_content, folder="order") + + if not checkout_information.abacus_ssh_upload_done: + abacus_ssh_upload_invoice(invoice_xml_filename, invoice_xml_content, folder="order") + checkout_information.abacus_ssh_upload_done = True + checkout_information.save() def create_invoice_xml(checkout_information: CheckoutInformation): @@ -210,20 +212,8 @@ def abacus_ssh_upload_invoice( filename: str, content_xml: str, folder: str = "" ) -> None: invoice_io = BytesIO(content_xml.encode("utf-8")) - ssh_client = SSHClient() - - print(invoice_io) - try: - ssh_client.set_missing_host_key_policy(AutoAddPolicy()) - ssh_client.connect( - hostname=settings.ABACUS_EXPORT_SFTP_HOST, - port=settings.ABACUS_EXPORT_SFTP_PORT, - username=settings.ABACUS_EXPORT_SFTP_USERNAME, - password=settings.ABACUS_EXPORT_SFTP_PASSWORD, - ) - - with ssh_client.open_sftp() as sftp_client: + with AbacusSftpClient() as sftp_client: path = filename if folder: path = f"{folder}/{filename}" @@ -232,5 +222,3 @@ def abacus_ssh_upload_invoice( except Exception as e: logger.error("Could not upload invoice", exc_info=e) - finally: - ssh_client.close() diff --git a/server/vbv_lernwelt/shop/invoice/abacus_sftp_client.py b/server/vbv_lernwelt/shop/invoice/abacus_sftp_client.py new file mode 100644 index 00000000..ae44b053 --- /dev/null +++ b/server/vbv_lernwelt/shop/invoice/abacus_sftp_client.py @@ -0,0 +1,26 @@ +from django.conf import settings +from paramiko.client import SSHClient, AutoAddPolicy + + +def _create_abacus_sftp_client(): + ssh_client = SSHClient() + + ssh_client.set_missing_host_key_policy(AutoAddPolicy()) + ssh_client.connect( + hostname=settings.ABACUS_EXPORT_SFTP_HOST, + port=settings.ABACUS_EXPORT_SFTP_PORT, + username=settings.ABACUS_EXPORT_SFTP_USERNAME, + password=settings.ABACUS_EXPORT_SFTP_PASSWORD, + ) + + return ssh_client.open_sftp(), ssh_client + + +class AbacusSftpClient: + def __enter__(self): + (self.sftp_client, self.ssh_client) = _create_abacus_sftp_client() + return self.sftp_client + + def __exit__(self, exc_type, exc_value, exc_traceback): + # self.sftp_client.close() + self.ssh_client.close() diff --git a/server/vbv_lernwelt/shop/migrations/0014_checkoutinformation_abacus_ssh_upload_done.py b/server/vbv_lernwelt/shop/migrations/0014_checkoutinformation_abacus_ssh_upload_done.py new file mode 100644 index 00000000..16c48297 --- /dev/null +++ b/server/vbv_lernwelt/shop/migrations/0014_checkoutinformation_abacus_ssh_upload_done.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.20 on 2024-05-31 15:17 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('shop', '0013_checkoutinformation_abacus_order_id'), + ] + + operations = [ + migrations.AddField( + model_name='checkoutinformation', + name='abacus_ssh_upload_done', + field=models.BooleanField(default=False), + ), + ] diff --git a/server/vbv_lernwelt/shop/models.py b/server/vbv_lernwelt/shop/models.py index 94139de7..af987a6e 100644 --- a/server/vbv_lernwelt/shop/models.py +++ b/server/vbv_lernwelt/shop/models.py @@ -101,6 +101,7 @@ class CheckoutInformation(models.Model): # is only set by abacus invoice export code abacus_order_id = models.BigIntegerField(unique=True, null=True, blank=True) + abacus_ssh_upload_done = models.BooleanField(default=False) def set_increment_abacus_order_id(self): if self.abacus_order_id: diff --git a/server/vbv_lernwelt/shop/tests/test_invoice.py b/server/vbv_lernwelt/shop/tests/test_abacus_invoice.py similarity index 83% rename from server/vbv_lernwelt/shop/tests/test_invoice.py rename to server/vbv_lernwelt/shop/tests/test_abacus_invoice.py index 10c384e1..4f295b00 100644 --- a/server/vbv_lernwelt/shop/tests/test_invoice.py +++ b/server/vbv_lernwelt/shop/tests/test_abacus_invoice.py @@ -1,5 +1,4 @@ from datetime import date, datetime -from unittest.mock import create_autospec from django.test import TestCase @@ -7,14 +6,11 @@ from vbv_lernwelt.core.admin import User from vbv_lernwelt.core.create_default_users import create_default_users from vbv_lernwelt.core.model_utils import add_countries from vbv_lernwelt.shop.invoice.abacus import ( - AbacusInvoiceCreator, create_customer_xml, create_invoice_xml, render_customer_xml, render_invoice_xml, ) -from vbv_lernwelt.shop.invoice.repositories import InvoiceRepository -from vbv_lernwelt.shop.models import CheckoutInformation from vbv_lernwelt.shop.tests.factories import CheckoutInformationFactory USER_USERNAME = "testuser" @@ -219,40 +215,3 @@ class AbacusInvoiceTestCase(TestCase): self.maxDiff = None self.assertXMLEqual(expected_xml, customer_xml) - - def test_create_invoice_calls_upload(self): - # GIVEN - repository_mock = create_autospec(InvoiceRepository) - - creator = AbacusInvoiceCreator(repository=repository_mock) - - expected_filename = "test.xml" - - checkout_information = CheckoutInformation.objects.create( - user=self.user, - transaction_id="12345", - product_sku="001", - product_name="Test Product", - product_description="Test Product Description", - product_price=1000, - state="initialized", - ) - - # WHEN - creator.create_invoice( - checkout_information=checkout_information, - filename=expected_filename, - ) - - # THEN - repository_mock.upload_invoice.assert_called_once() - uploaded_invoice, uploaded_filename = repository_mock.upload_invoice.call_args[ - 0 - ] - - assert uploaded_filename == expected_filename - assert "