Florian Mihalits ada8b7a200 Add payment types for walks and update related models
This commit introduces new payment types, including "MangoPay" and "Cash", to the localization resource files for both German and English. A new enum `WalkPaymentType` is created to represent these payment types, and the `Walk` class is updated to include a required `PaymentType` property of this enum type.

The database context is modified to add a `PaymentType` column to the `Walks` table, accompanied by a migration to implement this change and set a default value. The `WalkController` is updated to support filtering by the new `PaymentType` field, and the mapping profile is enhanced to include mappings for `WalkPaymentType` and its view model counterpart.

View models are also updated to incorporate the new `PaymentType` and its display name. Front-end TypeScript files are adjusted to utilize the new `WalkPaymentType` enum and related properties, while the UI is enhanced to display the payment type in the details view of walks. Version numbers in various configuration files are incremented to reflect these changes.
2025-06-30 11:06:30 +02:00

459 lines
22 KiB
TypeScript

import { Global } from "../../Core/Globals"
import { EventManager } from "../../Core/Events"
import { Ui } from "../../Core/Ui"
import * as Events from "../../Core/EventDefinitions"
import { LocalizationManager } from "../../Core/Localization"
import { Grid, Sort, Filter, Page, RowSelectEventArgs, Resize, ColumnChooser, Reorder } from "@syncfusion/ej2-grids"
import { Query, DataManager, UrlAdaptor } from "@syncfusion/ej2-data";
import * as Models from "../../Common/Models";
import * as Utility from "../../Core/Utility";
import { setValue, getValue } from "@syncfusion/ej2-base";
import { createElement } from "@syncfusion/ej2-base";
import { DropDownList } from "@syncfusion/ej2-dropdowns";
import WalkWithNamesListItem = Models.WalkWithNamesListItem;
import WalkType = Models.WalkType;
import WalkStatus = Models.WalkStatus;
import WalkServiceType = Models.WalkServiceType;
import PaymentStatus = Models.PaymentStatus;
import WalkPaymentType = Models.WalkPaymentType;
class WalksModule {
private _moduleName = "WalksModule";
private _component = "#walksContainer";
private _global: Global;
private _eventManager: EventManager;
private _uiManager: Ui;
private _localizationMananger: LocalizationManager;
private _data: any;
private _grid: Grid;
private _dataManager: DataManager;
private _typeList;
private _filterType: DropDownList;
private _serviceTypeList;
private _filterServiceType: DropDownList;
private _statusList;
private _filterStatus: DropDownList;
private _paymentStatusList;
private _filterPaymentStatus: DropDownList;
private _paymentTypeList;
private _filterPaymentType: DropDownList;
constructor() {
this._global = Global.getInstance();
this._eventManager = EventManager.getInstance();
this._uiManager = Ui.getInstance();
this._localizationMananger = LocalizationManager.getInstance();
Grid.Inject(Sort, Filter, Page, Resize, ColumnChooser, Reorder);
}
/**
* Stellt einen Prefix vor einen Selektor
* @param selector
*/
private prefix(selector: string): string {
const self = this;
if ($(`${self._component} ${selector}`).length > 0)
return `${self._component} ${selector}`;
return `html ${selector}`;
}
/**
* Einstellungen für die Liste machen
*/
private setupList(): void {
var self = this;
self._typeList = [
{ value: WalkType.Scheduled, text: self._localizationMananger.get("WalkType_Scheduled")},
{ value: WalkType.Direct, text: self._localizationMananger.get("WalkType_Direct") },
{ value: WalkType.OpenRequest, text: self._localizationMananger.get("WalkType_OpenRequest") }];
self._serviceTypeList = [
{ value: WalkServiceType.Walking, text: self._localizationMananger.get("WalkServiceType_Walking")},
{ value: WalkServiceType.Sitting, text: self._localizationMananger.get("WalkServiceType_Sitting") },
{ value: WalkServiceType.DayCare, text: self._localizationMananger.get("WalkServiceType_DayCare") }];
self._statusList = [
{ value: WalkStatus.Requested, text: self._localizationMananger.get("WalkStatus_Requested")},
{ value: WalkStatus.Accepted, text: self._localizationMananger.get("WalkStatus_Accepted") },
{ value: WalkStatus.Declined, text: self._localizationMananger.get("WalkStatus_Declined") },
{ value: WalkStatus.Cancelled, text: self._localizationMananger.get("WalkStatus_Cancelled") },
{ value: WalkStatus.Started, text: self._localizationMananger.get("WalkStatus_Started") },
{ value: WalkStatus.Completed, text: self._localizationMananger.get("WalkStatus_Completed") },
{ value: WalkStatus.Confirmed, text: self._localizationMananger.get("WalkStatus_Confirmed") },
{ value: WalkStatus.Complained, text: self._localizationMananger.get("WalkStatus_Complained") },
{ value: WalkStatus.ComplainResolved, text: self._localizationMananger.get("WalkStatus_ComplainResolved") }];
self._paymentStatusList = [
{ value: PaymentStatus.None, text: self._localizationMananger.get("PaymentStatus_None")},
{ value: PaymentStatus.Pending, text: self._localizationMananger.get("PaymentStatus_Pending") },
{ value: PaymentStatus.Authorized, text: self._localizationMananger.get("PaymentStatus_Authorized") },
{ value: PaymentStatus.Paid, text: self._localizationMananger.get("PaymentStatus_Paid") },
{ value: PaymentStatus.Refunded, text: self._localizationMananger.get("PaymentStatus_Refunded") },
{ value: PaymentStatus.Voided, text: self._localizationMananger.get("PaymentStatus_Voided") },
{ value: PaymentStatus.RefundendAndPayed, text: self._localizationMananger.get("PaymentStatus_RefundendAndPayed") }];
self._paymentTypeList = [
{ value: WalkPaymentType.MangoPay, text: self._localizationMananger.get("WalkPaymentType_MangoPay")},
{ value: WalkPaymentType.Cash, text: self._localizationMananger.get("WalkPaymentType_Cash") }];
self._dataManager = new DataManager({
url: <string>$(self.prefix("#jsGetUrl")).val(),
adaptor: new UrlAdaptor(),
crossDomain: true,
headers: [{ "X-Requested-With": "XmlHttpRequest" }]
});
self._uiManager.blockUi(self.prefix("#listContainer"));
var query = new Query().addParams("searchValue", $(self.prefix("#search")).valForSearch());
self._grid = new Grid({
dataSource: self._dataManager,
query: query,
height: 450,
enablePersistence: true,
allowSorting: true,
allowFiltering: true,
allowPaging: true,
allowResizing: true,
allowReordering: true,
showColumnChooser: true,
gridLines: "Vertical",
filterSettings: { type: "Menu" },
sortSettings: { columns: [{ field: "created", direction: <any>"Descending" }] },
selectionSettings: { checkboxMode: "ResetOnRowClick", checkboxOnly: false },
columns: <any>[
{ field: "select", type: "checkbox", width: 50, allowFiltering: false, allowSorting: false, showInColumnChooser: false, allowReordering: false },
{ field: "id", type: "string", visible: false, showInColumnChooser: false },
{ field: "created", headerText: self._localizationMananger.get("Common_Created"), type: "datetime", format: { type: "dateTime", skeleton: "medium" } },
{
field: "type", headerText: self._localizationMananger.get("Walk_Type"), type: "number", template: "${typeText}",
filter: {
ui: {
create: (args: { target: Element, column: Object }) => {
let flValInput: HTMLElement = createElement("input", { className: "flm-input" });
args.target.appendChild(flValInput);
self._filterType = new DropDownList({
dataSource: new DataManager(self._typeList),
fields: { text: "text", value: "value" },
placeholder: self._localizationMananger.get("Common_Select"),
popupHeight: "200px"
});
self._filterType.appendTo(flValInput);
},
write: (args: {
column: Object, target: Element, parent: any, filteredValue: number | string
}) => {
self._filterType.value = args.filteredValue;
},
read: (args: { target: Element, column: any, operator: string, fltrObj: Filter }) => {
args.fltrObj.filterByColumn(args.column.field, args.operator, self._filterType.value);
}
}
}
},
{
field: "serviceType", headerText: self._localizationMananger.get("Walk_ServiceType"), type: "number", template: "${serviceTypeText}",
filter: {
ui: {
create: (args: { target: Element, column: Object }) => {
let flValInput: HTMLElement = createElement("input", { className: "flm-input" });
args.target.appendChild(flValInput);
self._filterServiceType = new DropDownList({
dataSource: new DataManager(self._serviceTypeList),
fields: { text: "text", value: "value" },
placeholder: self._localizationMananger.get("Common_Select"),
popupHeight: "200px"
});
self._filterServiceType.appendTo(flValInput);
},
write: (args: {
column: Object, target: Element, parent: any, filteredValue: number | string
}) => {
self._filterServiceType.value = args.filteredValue;
},
read: (args: { target: Element, column: any, operator: string, fltrObj: Filter }) => {
args.fltrObj.filterByColumn(args.column.field, args.operator, self._filterServiceType.value);
}
}
}
},
{
field: "status", headerText: self._localizationMananger.get("Walk_Status"), type: "number", template: "${statusText}",
filter: {
ui: {
create: (args: { target: Element, column: Object }) => {
let flValInput: HTMLElement = createElement("input", { className: "flm-input" });
args.target.appendChild(flValInput);
self._filterStatus = new DropDownList({
dataSource: new DataManager(self._statusList),
fields: { text: "text", value: "value" },
placeholder: self._localizationMananger.get("Common_Select"),
popupHeight: "200px"
});
self._filterStatus.appendTo(flValInput);
},
write: (args: {
column: Object, target: Element, parent: any, filteredValue: number | string
}) => {
self._filterStatus.value = args.filteredValue;
},
read: (args: { target: Element, column: any, operator: string, fltrObj: Filter }) => {
args.fltrObj.filterByColumn(args.column.field, args.operator, self._filterStatus.value);
}
}
}
},
{
field: "paymentType", headerText: self._localizationMananger.get("Walk_PaymentType"), type: "number", template: "${paymentTypeText}",
filter: {
ui: {
create: (args: { target: Element, column: Object }) => {
let flValInput: HTMLElement = createElement("input", { className: "flm-input" });
args.target.appendChild(flValInput);
self._filterPaymentType = new DropDownList({
dataSource: new DataManager(self._paymentTypeList),
fields: { text: "text", value: "value" },
placeholder: self._localizationMananger.get("Common_Select"),
popupHeight: "200px"
});
self._filterPaymentType.appendTo(flValInput);
},
write: (args: {
column: Object, target: Element, parent: any, filteredValue: number | string
}) => {
self._filterPaymentType.value = args.filteredValue;
},
read: (args: { target: Element, column: any, operator: string, fltrObj: Filter }) => {
args.fltrObj.filterByColumn(args.column.field, args.operator, self._filterPaymentType.value);
}
}
}
},
{
field: "paymentStatus", headerText: self._localizationMananger.get("Walk_PaymentStatus"), type: "number", template: "${paymentStatusText}",
filter: {
ui: {
create: (args: { target: Element, column: Object }) => {
let flValInput: HTMLElement = createElement("input", { className: "flm-input" });
args.target.appendChild(flValInput);
self._filterPaymentStatus = new DropDownList({
dataSource: new DataManager(self._paymentStatusList),
fields: { text: "text", value: "value" },
placeholder: self._localizationMananger.get("Common_Select"),
popupHeight: "200px"
});
self._filterPaymentStatus.appendTo(flValInput);
},
write: (args: {
column: Object, target: Element, parent: any, filteredValue: number | string
}) => {
self._filterPaymentStatus.value = args.filteredValue;
},
read: (args: { target: Element, column: any, operator: string, fltrObj: Filter }) => {
args.fltrObj.filterByColumn(args.column.field, args.operator, self._filterPaymentStatus.value);
}
}
}
},
{ field: "dogOwnerName", headerText: self._localizationMananger.get("Walk_DogOwnerName"), type: "string" },
{ field: "dogWalkerName", headerText: self._localizationMananger.get("Walk_DogWalkerName"), type: "string" },
{ field: "dogCount", headerText: self._localizationMananger.get("Walk_DogCount"), type: "number", format: "n0", textAlign: "right" },
{ field: "pickupAddress_Zip", headerText: self._localizationMananger.get("Address_Zip"), type: "string" },
{ field: "pickupAddress_City", headerText: self._localizationMananger.get("Address_City"), type: "string" },
{ field: "pickupAddress_CountryCode", headerText: self._localizationMananger.get("Common_Country"), type: "string" },
{ field: "price", headerText: self._localizationMananger.get("Walk_Price"), type: "number", format: "n2", textAlign: "right" },
{ field: "start", headerText: self._localizationMananger.get("Walk_Start"), type: "datetime", format: { type: "dateTime", skeleton: "medium" } },
{ field: "end", headerText: self._localizationMananger.get("Walk_End"), type: "datetime", format: { type: "dateTime", skeleton: "medium" } },
{ field: "started", headerText: self._localizationMananger.get("Walk_Started"), type: "datetime", format: { type: "dateTime", skeleton: "medium" } },
{ field: "completed", headerText: self._localizationMananger.get("Walk_Completed"), type: "datetime", format: { type: "dateTime", skeleton: "medium" } },
{ field: "confirmed", headerText: self._localizationMananger.get("Walk_Confirmed"), type: "datetime", format: { type: "dateTime", skeleton: "medium" } },
{ field: "complained", headerText: self._localizationMananger.get("Walk_Complained"), type: "datetime", format: { type: "dateTime", skeleton: "medium" } },
],
pageSettings: { pageSizes: [5, 10, 25, 50, 100], pageSize: 50 },
rowSelected: function (args: RowSelectEventArgs) {
self.handleSelection();
},
rowDeselected: function (args: any) {
self.handleSelection();
},
recordDoubleClick: function (args: any) {
self._grid.clearSelection();
if (args.rowIndex !== undefined) {
self._grid.selectRow(args.rowIndex);
self.details();
}
},
headerCellInfo: function (args) {
if (args.cell.column.field !== "select") {
const toolcontent = args.cell.column.headerText;
args.node.setAttribute("title", toolcontent);
args.node.setAttribute("data-placement", "top");
args.node.setAttribute("data-toggle", "tooltip");
}
},
dataBound: function () {
const cloned = getValue("addOnPersist", self._grid);
setValue("addOnPersist", function (key) {
key = key.filter(item => item !== "selectedRowIndex");
return cloned.call(this, key);
}, self._grid);
setTimeout(() => { $(".tooltip").remove(); $('[data-toggle="tooltip"]').tooltip(); }, 500);
},
actionComplete: function (args) {
if (args.requestType === "filterafteropen") {
if (args.filterModel.dlgDiv.querySelector(".e-autocomplete")) {
var autoObject = args.filterModel.dlgDiv.querySelector(".e-autocomplete").ej2_instances[0];
autoObject.autofill = false;
autoObject.minLength = 20;
autoObject.open = args => { args.popup.element.classList.add("d-none"); };
}
}
}
});
self._grid.appendTo(self.prefix("#gridWalks"));
self._uiManager.unblockUi(self.prefix("#listContainer"));
}
/**
* Bei Auswahl oder abwahl einer Row...
*/
private handleSelection(): void {
var self = this;
let rows = self._grid.getSelectedRecords() as Array<WalkWithNamesListItem>;
$(self.prefix("#btnDetails")).prop("disabled", true);
if (rows.length === 0) {
$(self.prefix("#btnDetails")).prop("disabled", true);
}
else {
if (rows.length === 1)
$(self.prefix("#btnDetails")).prop("disabled", false);
else
$(self.prefix("#btnDetails")).prop("disabled", true);
}
}
/**
* Laden der Einträge
*/
private loadItems(): void {
var self = this;
self._grid.clearSelection();
self._grid.refresh();
self.handleSelection();
}
/**
* Behandeln der Suche via Feld
*/
private _oldSearch = "";
private handleSearch(): void {
var self = this;
if ($(self.prefix("#search")).doSearch()) {
var search = $(self.prefix("#search")).valForSearch();
if (self._oldSearch !== search) {
self._oldSearch = search;
self._grid.clearSelection();
self._grid.query = new Query().addParams("searchValue", $(self.prefix("#search")).valForSearch());
self.handleSelection();
}
}
}
/**
* Details
*/
private details(): void {
var self = this;
self._uiManager.blockMainUi();
$.ajax({
url: <string>$(self.prefix("#jsDetailsUrl")).val(),
data: <any>{ id: (<WalkWithNamesListItem>self._grid.getSelectedRecords()[0]).id },
dataType: "HTML",
type: "GET",
success(data) {
$(self.prefix("#detail")).html(data);
$(self.prefix("#list")).fadeOut("slow", () => {
$(self.prefix("#detail")).fadeIn("slow");
});
},
error() {
},
complete() {
self._uiManager.unblockMainUi();
}
});
}
/**
* Bindungen erstellen
*/
private setupBindings(): void {
var self = this;
$(self.prefix("#btnColumns")).off("click").on("click", function () {
self._grid.columnChooserModule.openColumnChooser();
});
$(self.prefix("#btnDetails")).off("click").on("click", function () {
self.details();
});
$(self.prefix("#search")).on("keyup change", Utility.debounce(() => {
self.handleSearch();
}, 500));
self._eventManager.subscribe(Events.Walk.detailsCancel, self._moduleName, function () {
$(self.prefix("#detail")).fadeOut("slow", () => {
$(self.prefix("#list")).fadeIn();
$(self.prefix("#detail")).html("");
});
});
self._eventManager.subscribe(Events.Walk.detailsSuccess, self._moduleName, function () {
self.loadItems();
$(self.prefix("#detail")).fadeOut("slow", () => {
$(self.prefix("#list")).fadeIn();
$(self.prefix("#detail")).html("");
});
});
}
/**
* Aktivieren des Moduls
*/
public activate(): void {
var self = this;
$.setupValidator();
$.reinitializeValidator();
this.setupBindings();
self.setupList();
$.randomAutocompleteValue();
}
/**
* Initialisieren des Moduls
*/
public init(): void {
if (this._global.appInitialized) {
walksModule.activate();
} else {
setTimeout(() => { this.init(); }, 10);
}
}
}
let walksModule = new WalksModule();
walksModule.init();