Sort person by paid year

This commit is contained in:
Elia Bieri 2024-09-12 17:07:52 +02:00
parent c65c1be0a8
commit 0a4bbb0df7
6 changed files with 88 additions and 6 deletions

View File

@ -10,6 +10,7 @@ import { useUserStore } from "@/stores/user";
import type { DashboardPersonsPageMode, StatisticsFilterItem } from "@/types";
import { exportDataAsXls } from "@/utils/export";
import { useRouteQuery } from "@vueuse/router";
import dayjs from "dayjs";
import { useTranslation } from "i18next-vue";
import _ from "lodash";
import log from "loglevel";
@ -160,7 +161,7 @@ const roles = computed(() => {
return [
{
id: "",
id: UNFILTERED,
name: `${t("Rolle")}: ${t("a.Alle")}`,
},
...values,
@ -191,6 +192,30 @@ const chosenProfiles = computed(() => {
});
const selectedChosenProfile = ref<MenuItem>(chosenProfiles.value[0]);
const paidYears = computed(() => {
const values = _(dashboardPersons.value)
.filter((cs) => dayjs(cs.paid_date).isValid())
.map((cs) => {
const paidYear = dayjs(cs.paid_date).format("YYYY");
return Object.assign({}, cs, {
name: paidYear,
id: paidYear,
});
})
.uniqBy("id")
.orderBy("name")
.value();
return [
{
id: UNFILTERED,
name: `${t("Jahr")}: ${t("a.Alle")}`,
},
...values,
];
});
const selectedPaidYear = ref<MenuItem>(paidYears.value[0]);
const filteredPersons = computed(() => {
return _.orderBy(
dashboardPersons.value
@ -225,7 +250,7 @@ const filteredPersons = computed(() => {
);
})
.filter((person) => {
if (selectedRole.value.id === "") {
if (selectedRole.value.id === UNFILTERED) {
return true;
}
return person.course_sessions.some(
@ -233,10 +258,17 @@ const filteredPersons = computed(() => {
);
})
.filter((person) => {
if (selectedChosenProfile.value.id === "") {
if (selectedChosenProfile.value.id === UNFILTERED) {
return true;
}
return person.chosen_profile === selectedChosenProfile.value.id;
})
.filter((person) => {
if (selectedPaidYear.value.id === UNFILTERED) {
return true;
}
const paidYear = dayjs(person.paid_date).format("YYYY");
return paidYear == selectedPaidYear.value.id;
}),
["last_name", "first_name"]
);
@ -249,7 +281,8 @@ const filtersVisible = computed(() => {
regions.value.length > 2 ||
generations.value.length > 2 ||
roles.value.length > 2 ||
chosenProfiles.value.length > 2
chosenProfiles.value.length > 2 ||
paidYears.value.length > 2
);
});
@ -380,6 +413,14 @@ watch(selectedRegion, () => {
:items="chosenProfiles"
borderless
></ItDropdownSelect>
<ItDropdownSelect
v-if="paidYears.length > 2"
v-model="selectedPaidYear"
data-cy="select-paid-year"
:items="paidYears"
borderless
></ItDropdownSelect>
</section>
<div
v-for="person in filteredPersons"

View File

@ -21,6 +21,7 @@ import type {
XlsExportRequestData,
XlsExportResponseData,
} from "@/types";
import type dayjs from "dayjs";
export type DashboardPersonRoleType =
| "SUPERVISOR"
@ -78,11 +79,12 @@ export type DashboardPersonType = {
course_sessions: DashboardPersonCourseSessionType[];
avatar_url: string;
avatar_url_small: string;
chosen_profile: string;
competence_metrics?: {
passed_count: number;
failed_count: number;
};
chosen_profile: string;
paid_date?: string;
};
export type DashboardCourseConfigType = {

View File

@ -1,4 +1,5 @@
import uuid
from datetime import datetime
from enum import Enum
from django.db import models
@ -13,6 +14,7 @@ from vbv_lernwelt.core.model_utils import find_available_slug
from vbv_lernwelt.core.models import User
from vbv_lernwelt.course.serializer_helpers import get_course_serializer_class
from vbv_lernwelt.files.models import UploadFile
from vbv_lernwelt.shop.models import CheckoutState
class CircleContactType(Enum):
@ -301,6 +303,16 @@ class CourseSessionUser(models.Model):
]
ordering = ["user__last_name", "user__first_name", "user__email"]
@property
def paid_date(self) -> datetime | None:
"""
Returns the date when the user paid for the course session
"""
checkout = self.user.checkout_information.filter(state=CheckoutState.PAID)
if checkout:
return checkout.first().created_at
return None
def __str__(self):
return f"{self.user} ({self.course_session.title})"

View File

@ -109,6 +109,7 @@ def create_person_list_with_roles(
"avatar_url": user_object.avatar_url,
"course_sessions": [],
"chosen_profile": csu.chosen_profile.code if csu.chosen_profile else "all",
"paid_date": csu.paid_date.isoformat() if csu.paid_date else None,
}
if include_private_data:
user_data["phone_number"] = user_object.phone_number

View File

@ -0,0 +1,24 @@
# Generated by Django 4.2.13 on 2024-09-12 13:40
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
("shop", "0019_alter_checkoutinformation_refno2"),
]
operations = [
migrations.AlterField(
model_name="checkoutinformation",
name="user",
field=models.ForeignKey(
on_delete=django.db.models.deletion.PROTECT,
related_name="checkout_information",
to=settings.AUTH_USER_MODEL,
),
),
]

View File

@ -43,7 +43,9 @@ class CheckoutInformation(models.Model):
(INVOICE_ADDRESS_ORGANISATION, "Organisation"),
)
user = models.ForeignKey("core.User", on_delete=models.PROTECT)
user = models.ForeignKey(
"core.User", on_delete=models.PROTECT, related_name="checkout_information"
)
product_sku = models.CharField(max_length=255)
product_name = models.CharField(max_length=255)