Test cembra/byjuno invoice

This commit is contained in:
Daniel Egger 2024-06-27 18:09:21 +02:00
parent 052b07eabf
commit fab9297989
7 changed files with 75 additions and 39 deletions

View File

@ -1,6 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed } from "vue"; import { computed } from "vue";
import { useEntities } from "@/services/entities"; import { useEntities } from "@/services/entities";
import { t } from "i18next";
const props = defineProps<{ const props = defineProps<{
modelValue: { modelValue: {
@ -11,6 +12,7 @@ const props = defineProps<{
postal_code: string; postal_code: string;
city: string; city: string;
country_code: string; country_code: string;
payment_method: string;
phone_number: string; phone_number: string;
birth_date: string; birth_date: string;
@ -21,6 +23,13 @@ const emit = defineEmits(["update:modelValue"]);
const { countries } = useEntities(); const { countries } = useEntities();
const paymentMethods = computed(() => {
return [
{ value: "credit_card", label: t("a.Debit-/Kreditkarte / Twint") },
{ value: "cembra_byjuno", label: t("a.Rechnung") },
];
});
const address = computed({ const address = computed({
get() { get() {
return props.modelValue; return props.modelValue;
@ -168,6 +177,35 @@ const address = computed({
</div> </div>
<div class="col-span-full"> <div class="col-span-full">
<label
for="paymentMethod"
class="block text-sm font-medium leading-6 text-gray-900"
>
{{ $t("a.Zahlungsart") }}
</label>
<div class="mt-2">
<select
id="paymentMethod"
v-model="address.payment_method"
required
name="paymentMethod"
class="block w-full border-0 py-1.5 text-gray-900 ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-inset focus:ring-blue-600 sm:text-sm sm:leading-6"
>
<option v-for="pm in paymentMethods" :key="pm.value" :value="pm.value">
{{ pm.label }}
</option>
</select>
</div>
</div>
<div v-if="address.payment_method === 'cembra_byjuno'" class="col-span-full">
<p class="mt-4">
TODO: Eine Zahlung mit Rechnung wird immer auf deine Privatadresse verrechnet.
Du musst zwinged deine Telefonnummer und dein Geburtsdatum angeben.
</p>
</div>
<div v-if="address.payment_method === 'cembra_byjuno'" class="col-span-full">
<label for="phone" class="block text-sm font-medium leading-6 text-gray-900"> <label for="phone" class="block text-sm font-medium leading-6 text-gray-900">
{{ $t("a.Telefonnummer") }} {{ $t("a.Telefonnummer") }}
</label> </label>
@ -183,7 +221,7 @@ const address = computed({
</div> </div>
</div> </div>
<div class="col-span-full"> <div v-if="address.payment_method === 'cembra_byjuno'" class="col-span-full">
<label for="birth-date" class="block text-sm font-medium leading-6 text-gray-900"> <label for="birth-date" class="block text-sm font-medium leading-6 text-gray-900">
{{ $t("a.Geburtsdatum") }} {{ $t("a.Geburtsdatum") }}
</label> </label>

View File

@ -1,6 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import WizardPage from "@/components/onboarding/WizardPage.vue"; import WizardPage from "@/components/onboarding/WizardPage.vue";
import { computed, ref } from "vue"; import { computed, ref, watch } from "vue";
import { type User, useUserStore } from "@/stores/user"; import { type User, useUserStore } from "@/stores/user";
import PersonalAddress from "@/components/onboarding/PersonalAddress.vue"; import PersonalAddress from "@/components/onboarding/PersonalAddress.vue";
import OrganisationAddress from "@/components/onboarding/OrganisationAddress.vue"; import OrganisationAddress from "@/components/onboarding/OrganisationAddress.vue";
@ -56,6 +56,8 @@ const address = ref({
city: user.city, city: user.city,
country_code: user.country?.country_code ?? "CH", country_code: user.country?.country_code ?? "CH",
payment_method: "credit_card",
phone_number: user.phone_number, phone_number: user.phone_number,
birth_date: user.birth_date, birth_date: user.birth_date,
@ -75,14 +77,14 @@ const setWithCompanyAddress = (value: boolean) => {
address.value.invoice_address = value ? "org" : "prv"; address.value.invoice_address = value ? "org" : "prv";
}; };
const withCembraInvoice = ref<boolean>(false); watch(
() => address.value.payment_method,
function toggleCembraInvoice() { (newValue) => {
withCembraInvoice.value = !withCembraInvoice.value; if (newValue === "cembra_byjuno") {
if (!withCembraInvoice.value) {
setWithCompanyAddress(false); setWithCompanyAddress(false);
} }
} }
);
type FormErrors = { type FormErrors = {
personal: string[]; personal: string[];
@ -136,7 +138,7 @@ function validateAddress() {
} }
} }
if (withCembraInvoice.value) { if (address.value.payment_method === "cembra_byjuno") {
if (!address.value.phone_number) { if (!address.value.phone_number) {
formErrors.value.personal.push(t("a.Telefonnummer")); formErrors.value.personal.push(t("a.Telefonnummer"));
} }
@ -211,11 +213,14 @@ const executePayment = async () => {
address.value.phone_number = normalizeSwissPhoneNumber(address.value.phone_number); address.value.phone_number = normalizeSwissPhoneNumber(address.value.phone_number);
} }
const addressData = Object.assign({}, address.value);
delete addressData.payment_method;
itPost("/api/shop/vv/checkout/", { itPost("/api/shop/vv/checkout/", {
redirect_url: fullHost, redirect_url: fullHost,
address: address.value, address: addressData,
product: props.courseType, product: props.courseType,
with_cembra_invoice: withCembraInvoice.value, with_cembra_byjuno_invoice: address.value.payment_method === "cembra_byjuno",
device_fingerprint_session_key: getLocalSessionKey(), device_fingerprint_session_key: getLocalSessionKey(),
}).then((res: any) => { }).then((res: any) => {
console.log("Going to next page", res.next_step_url); console.log("Going to next page", res.next_step_url);
@ -227,7 +232,9 @@ const executePayment = async () => {
<template> <template>
<WizardPage :step="2"> <WizardPage :step="2">
<template #content> <template #content>
<DatatransCembraDeviceFingerprint v-if="withCembraInvoice" /> <DatatransCembraDeviceFingerprint
v-if="address.payment_method === 'cembra_byjuno'"
/>
<h2 class="my-10" data-cy="account-checkout-title"> <h2 class="my-10" data-cy="account-checkout-title">
{{ $t("a.Lehrgang kaufen") }} {{ $t("a.Lehrgang kaufen") }}
</h2> </h2>
@ -281,24 +288,7 @@ const executePayment = async () => {
{{ formErrors.personal.join(", ") }} {{ formErrors.personal.join(", ") }}
</p> </p>
<div class="flex flex-row items-center space-x-2 text-sm"> <section v-if="address.payment_method !== 'cembra_byjuno'">
<ItToggleSwitch
data-cy="cembra-switch"
:initially-switched-left="withCembraInvoice"
@click="toggleCembraInvoice()"
></ItToggleSwitch>
<span :class="{ 'text-gray-700': !withCembraInvoice }">
"TODO: Zahlung auf Rechnung/Cembra"
</span>
</div>
<section v-if="withCembraInvoice">
<p class="mt-4">
TODO: Bei Zahlung auf Rechnung/Cembra, wird immer auf deine Privatadresse
verrechnet. Du musst zwinged deine Telefonnummer und Geburtsdatum angeben.
</p>
</section>
<section v-else>
<div class="mt-4"> <div class="mt-4">
<button <button
v-if="!withCompanyAddress" v-if="!withCompanyAddress"
@ -362,7 +352,9 @@ const executePayment = async () => {
data-cy="continue-pay" data-cy="continue-pay"
@click="executePayment" @click="executePayment"
> >
<template v-if="withCembraInvoice">TODO: Mit Rechnung/Cembra bezahlen</template> <template v-if="address.payment_method === 'cembra_byjuno'">
{{ $t("a.Mit Rechnung bezahlen") }}
</template>
<template v-else> <template v-else>
{{ $t("a.Mit Kreditkarte bezahlen") }} {{ $t("a.Mit Kreditkarte bezahlen") }}
</template> </template>

View File

@ -119,7 +119,8 @@ describe("checkout.cy.js", () => {
"contain", "contain",
"Lehrgang kaufen" "Lehrgang kaufen"
); );
cy.get('[data-cy="cembra-switch"]').click();
cy.get('#paymentMethod').select('cembra_byjuno');
cy.get("#street-address").type("Eggersmatt"); cy.get("#street-address").type("Eggersmatt");
cy.get("#street-number").type("32"); cy.get("#street-number").type("32");
@ -169,7 +170,7 @@ describe("checkout.cy.js", () => {
expect(ci.country).to.equal("CH"); expect(ci.country).to.equal("CH");
expect(ci.phone_number).to.equal("+41792018586"); expect(ci.phone_number).to.equal("+41792018586");
expect(ci.birth_date).to.equal("1982-06-09"); expect(ci.birth_date).to.equal("1982-06-09");
expect(ci.cembra_invoice).to.be.true; expect(ci.cembra_byjuno_invoice).to.be.true;
expect(ci.invoice_address).to.equal("prv"); expect(ci.invoice_address).to.equal("prv");

View File

@ -16,7 +16,7 @@ class Migration(migrations.Migration):
), ),
migrations.AddField( migrations.AddField(
model_name="checkoutinformation", model_name="checkoutinformation",
name="cembra_invoice", name="cembra_byjuno_invoice",
field=models.BooleanField(default=False), field=models.BooleanField(default=False),
), ),
migrations.AddField( migrations.AddField(

View File

@ -79,7 +79,7 @@ class CheckoutInformation(models.Model):
) )
# optional fields for cembra payment # optional fields for cembra payment
cembra_invoice = models.BooleanField(default=False) cembra_byjuno_invoice = models.BooleanField(default=False)
birth_date = models.DateField(null=True, blank=True) birth_date = models.DateField(null=True, blank=True)
phone_number = models.CharField(max_length=255, blank=True, default="") phone_number = models.CharField(max_length=255, blank=True, default="")
email = models.CharField(max_length=255, blank=True, default="") email = models.CharField(max_length=255, blank=True, default="")

View File

@ -80,6 +80,7 @@ def init_datatrans_transaction(
datatrans_customer_data: dict = None, datatrans_customer_data: dict = None,
datatrans_int_data: dict = None, datatrans_int_data: dict = None,
context_data: dict = None, context_data: dict = None,
with_cembra_byjuno_invoice: bool = False,
): ):
payload = { payload = {
# We use autoSettle=True, so that we don't have to settle the transaction: # We use autoSettle=True, so that we don't have to settle the transaction:
@ -97,6 +98,9 @@ def init_datatrans_transaction(
}, },
} }
# FIXME: test with working cembra byjuno invoice customer?
# if with_cembra_byjuno_invoice:
# payload["paymentMethods"] = ["INT"]
if datatrans_customer_data: if datatrans_customer_data:
payload["customer"] = datatrans_customer_data payload["customer"] = datatrans_customer_data
if datatrans_int_data: if datatrans_int_data:

View File

@ -111,14 +111,14 @@ def checkout_vv(request):
if checkouts.filter(state=CheckoutState.PAID).exists(): if checkouts.filter(state=CheckoutState.PAID).exists():
return next_step_response(url="/") return next_step_response(url="/")
with_cembra_invoice = request.data.get("with_cembra_invoice", False) with_cembra_byjuno_invoice = request.data.get("with_cembra_byjuno_invoice", False)
ip_address = request.META.get("REMOTE_ADDR") ip_address = request.META.get("REMOTE_ADDR")
email = request.user.email email = request.user.email
try: try:
datatrans_customer_data = None datatrans_customer_data = None
datatrans_int_data = None datatrans_int_data = None
if with_cembra_invoice: if with_cembra_byjuno_invoice:
if "fakeapi" not in settings.DATATRANS_API_ENDPOINT: if "fakeapi" not in settings.DATATRANS_API_ENDPOINT:
request.user.set_increment_abacus_debitor_number() request.user.set_increment_abacus_debitor_number()
@ -158,6 +158,7 @@ def checkout_vv(request):
datatrans_customer_data=datatrans_customer_data, datatrans_customer_data=datatrans_customer_data,
datatrans_int_data=datatrans_int_data, datatrans_int_data=datatrans_int_data,
context_data=context_data, context_data=context_data,
with_cembra_byjuno_invoice=with_cembra_byjuno_invoice,
) )
except InitTransactionException as e: except InitTransactionException as e:
if not settings.DEBUG: if not settings.DEBUG:
@ -193,7 +194,7 @@ def checkout_vv(request):
product_description=product.description, product_description=product.description,
email=email, email=email,
ip_address=ip_address, ip_address=ip_address,
cembra_invoice=with_cembra_invoice, cembra_byjuno_invoice=with_cembra_byjuno_invoice,
device_fingerprint_session_key=request.data.get( device_fingerprint_session_key=request.data.get(
"device_fingerprint_session_key", "" "device_fingerprint_session_key", ""
), ),