Test abacus sftp upload
This commit is contained in:
parent
c89914107f
commit
516079ba10
|
|
@ -23,6 +23,8 @@ EMAIL_BACKEND = "django.core.mail.backends.locmem.EmailBackend"
|
||||||
WHITENOISE_MANIFEST_STRICT = False
|
WHITENOISE_MANIFEST_STRICT = False
|
||||||
AWS_S3_FILE_OVERWRITE = True
|
AWS_S3_FILE_OVERWRITE = True
|
||||||
|
|
||||||
|
ABACUS_EXPORT_SFTP_PORT = 34343
|
||||||
|
|
||||||
|
|
||||||
class DisableMigrations(dict):
|
class DisableMigrations(dict):
|
||||||
def __contains__(self, item):
|
def __contains__(self, item):
|
||||||
|
|
|
||||||
|
|
@ -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 "<CustomerNumber>60000012</CustomerNumber>" in debi_content
|
||||||
|
assert "<Email>andreas.feuz@eiger-versicherungen.ch</Email>" in debi_content
|
||||||
|
|
||||||
|
order_filepath = (
|
||||||
|
self.tmppath + "/order/myVBV_orde_60000012_20240215083312_6000000124.xml"
|
||||||
|
)
|
||||||
|
self.assertTrue(os.path.exists(order_filepath))
|
||||||
|
with open(order_filepath) as order_file:
|
||||||
|
order_content = order_file.read()
|
||||||
|
assert "<ReferencePurchaseOrder>24021508331287484</ReferencePurchaseOrder>" in order_content
|
||||||
|
assert "<CustomerNumber>60000012</CustomerNumber>" in order_content
|
||||||
|
|
||||||
|
feuz_checkout_info.refresh_from_db()
|
||||||
|
self.assertTrue(feuz_checkout_info.abacus_ssh_upload_done)
|
||||||
|
|
||||||
|
# calling `abacus_ssh_upload` a second time will not upload order file again...
|
||||||
|
os.remove(debitor_filepath)
|
||||||
|
os.remove(order_filepath)
|
||||||
|
|
||||||
|
abacus_ssh_upload(feuz_checkout_info)
|
||||||
|
|
||||||
|
debitor_filepath = self.tmppath + "/debitor/myVBV_debi_60000012.xml"
|
||||||
|
self.assertTrue(os.path.exists(debitor_filepath))
|
||||||
|
|
||||||
|
order_filepath = (
|
||||||
|
self.tmppath + "/order/myVBV_orde_60000012_20240215083312_6000000124.xml"
|
||||||
|
)
|
||||||
|
self.assertFalse(os.path.exists(order_filepath))
|
||||||
|
|
@ -2,7 +2,9 @@ import hashlib
|
||||||
import hmac
|
import hmac
|
||||||
import json
|
import json
|
||||||
import threading
|
import threading
|
||||||
|
import time
|
||||||
|
|
||||||
|
import requests
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.http import HttpResponse, JsonResponse
|
from django.http import HttpResponse, JsonResponse
|
||||||
from django.shortcuts import redirect
|
from django.shortcuts import redirect
|
||||||
|
|
@ -15,80 +17,6 @@ from vbv_lernwelt.core.models import User
|
||||||
@csrf_exempt
|
@csrf_exempt
|
||||||
@django_view_authentication_exempt
|
@django_view_authentication_exempt
|
||||||
def fake_datatrans_api_view(request, api_url=""):
|
def fake_datatrans_api_view(request, api_url=""):
|
||||||
# if api_url == "/redirect":
|
|
||||||
# fake_tamedia_token = request.GET.get("token")
|
|
||||||
# pai = fake_tamedia_token.split(":")[1]
|
|
||||||
# sub = SubhubCustomerSubscription.objects.filter(id=pai).first()
|
|
||||||
#
|
|
||||||
# header = f"<h1>fake tamedia activation for {pai}"
|
|
||||||
#
|
|
||||||
# if not sub:
|
|
||||||
# return HttpResponse(
|
|
||||||
# content=f"""
|
|
||||||
# {header}
|
|
||||||
# <p>no subscription found</p>
|
|
||||||
# """,
|
|
||||||
# status=404,
|
|
||||||
# )
|
|
||||||
#
|
|
||||||
# if request.method == "GET":
|
|
||||||
# if (
|
|
||||||
# sub
|
|
||||||
# and sub.partner_status
|
|
||||||
# == SubhubCustomerSubscription.PARTNER_STATUS_ENROLLED
|
|
||||||
# ):
|
|
||||||
# return HttpResponse(
|
|
||||||
# content=f"""
|
|
||||||
# {header}
|
|
||||||
# <div>
|
|
||||||
# <input type="text" name="partnerSubscriptionId" value="{pai}">
|
|
||||||
# <form action="{create_register_url(fake_tamedia_token)}" method="post">
|
|
||||||
# <button type="submit" data-cy="register-button">Register</button>
|
|
||||||
# </form>
|
|
||||||
# </div>
|
|
||||||
# """,
|
|
||||||
# status=200,
|
|
||||||
# )
|
|
||||||
# else:
|
|
||||||
# return HttpResponse(
|
|
||||||
# content=f"""
|
|
||||||
# {header}
|
|
||||||
# <p>already activated</p>
|
|
||||||
# """,
|
|
||||||
# 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":
|
if api_url == "/v1/transactions" and request.method == "POST":
|
||||||
data = json.loads(request.body.decode("utf-8"))
|
data = json.loads(request.body.decode("utf-8"))
|
||||||
user = User.objects.get(id=data["user_id"])
|
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(
|
def call_transaction_complete_webhook(
|
||||||
webhook_url, transaction_id, datatrans_status="settled"
|
webhook_url, transaction_id, datatrans_status="settled"
|
||||||
):
|
):
|
||||||
import time
|
|
||||||
|
|
||||||
import requests
|
|
||||||
|
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
|
|
||||||
payload = {
|
payload = {
|
||||||
|
|
|
||||||
|
|
@ -4,12 +4,10 @@ from xml.dom import minidom
|
||||||
from xml.etree.ElementTree import Element, SubElement, tostring
|
from xml.etree.ElementTree import Element, SubElement, tostring
|
||||||
|
|
||||||
import structlog
|
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
|
from vbv_lernwelt.shop.models import CheckoutInformation
|
||||||
|
|
||||||
|
|
||||||
logger = structlog.get_logger(__name__)
|
logger = structlog.get_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -22,7 +20,11 @@ def abacus_ssh_upload(checkout_information: CheckoutInformation):
|
||||||
abacus_ssh_upload_invoice(
|
abacus_ssh_upload_invoice(
|
||||||
customer_xml_filename, customer_xml_content, folder="debitor"
|
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):
|
def create_invoice_xml(checkout_information: CheckoutInformation):
|
||||||
|
|
@ -210,20 +212,8 @@ def abacus_ssh_upload_invoice(
|
||||||
filename: str, content_xml: str, folder: str = ""
|
filename: str, content_xml: str, folder: str = ""
|
||||||
) -> None:
|
) -> None:
|
||||||
invoice_io = BytesIO(content_xml.encode("utf-8"))
|
invoice_io = BytesIO(content_xml.encode("utf-8"))
|
||||||
ssh_client = SSHClient()
|
|
||||||
|
|
||||||
print(invoice_io)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
ssh_client.set_missing_host_key_policy(AutoAddPolicy())
|
with AbacusSftpClient() as sftp_client:
|
||||||
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:
|
|
||||||
path = filename
|
path = filename
|
||||||
if folder:
|
if folder:
|
||||||
path = f"{folder}/{filename}"
|
path = f"{folder}/{filename}"
|
||||||
|
|
@ -232,5 +222,3 @@ def abacus_ssh_upload_invoice(
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error("Could not upload invoice", exc_info=e)
|
logger.error("Could not upload invoice", exc_info=e)
|
||||||
finally:
|
|
||||||
ssh_client.close()
|
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
@ -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),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -101,6 +101,7 @@ class CheckoutInformation(models.Model):
|
||||||
|
|
||||||
# is only set by abacus invoice export code
|
# is only set by abacus invoice export code
|
||||||
abacus_order_id = models.BigIntegerField(unique=True, null=True, blank=True)
|
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):
|
def set_increment_abacus_order_id(self):
|
||||||
if self.abacus_order_id:
|
if self.abacus_order_id:
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
from datetime import date, datetime
|
from datetime import date, datetime
|
||||||
from unittest.mock import create_autospec
|
|
||||||
|
|
||||||
from django.test import TestCase
|
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.create_default_users import create_default_users
|
||||||
from vbv_lernwelt.core.model_utils import add_countries
|
from vbv_lernwelt.core.model_utils import add_countries
|
||||||
from vbv_lernwelt.shop.invoice.abacus import (
|
from vbv_lernwelt.shop.invoice.abacus import (
|
||||||
AbacusInvoiceCreator,
|
|
||||||
create_customer_xml,
|
create_customer_xml,
|
||||||
create_invoice_xml,
|
create_invoice_xml,
|
||||||
render_customer_xml,
|
render_customer_xml,
|
||||||
render_invoice_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
|
from vbv_lernwelt.shop.tests.factories import CheckoutInformationFactory
|
||||||
|
|
||||||
USER_USERNAME = "testuser"
|
USER_USERNAME = "testuser"
|
||||||
|
|
@ -219,40 +215,3 @@ class AbacusInvoiceTestCase(TestCase):
|
||||||
|
|
||||||
self.maxDiff = None
|
self.maxDiff = None
|
||||||
self.assertXMLEqual(expected_xml, customer_xml)
|
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 "<CustomerNumber>12345</CustomerNumber>" in uploaded_invoice
|
|
||||||
assert "<ItemNumber>1</ItemNumber>" in uploaded_invoice
|
|
||||||
assert "<ProductNumber>001</ProductNumber>" in uploaded_invoice
|
|
||||||
assert "<QuantityOrdered>1</QuantityOrdered>" in uploaded_invoice
|
|
||||||
assert "<Text>Test Product Description</Text>" in uploaded_invoice
|
|
||||||
Loading…
Reference in New Issue