/*
This file is part of GNU Taler
(C) 2021-2024 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
GNU Taler; see the file COPYING. If not, see
*/
/**
*
* @author Sebastian Javier Marchano (sebasjm)
*/
import { Amounts } from "@gnu-taler/taler-util";
import { isAfter, isFuture } from "date-fns";
import * as yup from "yup";
import { PAYTO_REGEX } from "../utils/constants.js";
import { Amounts } from "@gnu-taler/taler-util";
import { MerchantBackend } from "../declaration.js";
// import { MerchantBackend } from "../declaration.js";
yup.setLocale({
mixed: {
default: "field_invalid",
},
number: {
min: ({ min }: any) => ({ key: "field_too_short", values: { min } }),
max: ({ max }: any) => ({ key: "field_too_big", values: { max } }),
},
});
function listOfPayToUrisAreValid(values?: (string | undefined)[]): boolean {
return !!values && values.every((v) => v && PAYTO_REGEX.test(v));
}
function currencyWithAmountIsValid(value?: string): boolean {
return !!value && Amounts.parse(value) !== undefined;
}
function currencyGreaterThan0(value?: string) {
if (value) {
try {
const [, amount] = value.split(":");
const intAmount = parseInt(amount, 10);
return intAmount > 0;
} catch {
return false;
}
}
return true;
}
export const InstanceSchema = yup.object().shape({
id: yup.string().required().meta({ type: "url" }),
name: yup.string().required(),
auth: yup.object().shape({
method: yup.string().matches(/^(external|token)$/),
token: yup.string().optional().nullable(),
}),
payto_uris: yup
.array()
.of(yup.string())
.min(1)
.meta({ type: "array" })
.test("payto", "{path} is not valid", listOfPayToUrisAreValid),
default_max_deposit_fee: yup
.string()
.required()
.test("amount", "the amount is not valid", currencyWithAmountIsValid)
.meta({ type: "amount" }),
default_max_wire_fee: yup
.string()
.required()
.test("amount", "{path} is not valid", currencyWithAmountIsValid)
.meta({ type: "amount" }),
default_wire_fee_amortization: yup.number().required(),
address: yup
.object()
.shape({
country: yup.string().optional(),
address_lines: yup.array().of(yup.string()).max(7).optional(),
building_number: yup.string().optional(),
building_name: yup.string().optional(),
street: yup.string().optional(),
post_code: yup.string().optional(),
town_location: yup.string().optional(),
town: yup.string(),
district: yup.string().optional(),
country_subdivision: yup.string().optional(),
})
.meta({ type: "group" }),
jurisdiction: yup
.object()
.shape({
country: yup.string().optional(),
address_lines: yup.array().of(yup.string()).max(7).optional(),
building_number: yup.string().optional(),
building_name: yup.string().optional(),
street: yup.string().optional(),
post_code: yup.string().optional(),
town_location: yup.string().optional(),
town: yup.string(),
district: yup.string().optional(),
country_subdivision: yup.string().optional(),
})
.meta({ type: "group" }),
// default_pay_delay: yup.object()
// .shape({ d_us: yup.number() })
// .required()
// .meta({ type: 'duration' }),
// .transform(numberToDuration),
default_wire_transfer_delay: yup
.object()
.shape({ d_us: yup.number() })
.required()
.meta({ type: "duration" }),
// .transform(numberToDuration),
});
export const InstanceUpdateSchema = InstanceSchema.clone().omit(["id"]);
export const InstanceCreateSchema = InstanceSchema.clone();
export const OrderCreateSchema = yup.object().shape({
pricing: yup
.object()
.required()
.shape({
summary: yup.string().ensure().required(),
order_price: yup
.string()
.ensure()
.required()
.test("amount", "the amount is not valid", currencyWithAmountIsValid)
.test(
"amount_positive",
"the amount should be greater than 0",
currencyGreaterThan0,
),
}),
// extra: yup.object().test("extra", "is not a JSON format", stringIsValidJSON),
payments: yup
.object()
.required()
.shape({
refund_deadline: yup
.date()
.test("future", "should be in the future", (d) =>
d ? isFuture(d) : true,
),
pay_deadline: yup
.date()
.test("future", "should be in the future", (d) =>
d ? isFuture(d) : true,
),
auto_refund_deadline: yup
.date()
.test("future", "should be in the future", (d) =>
d ? isFuture(d) : true,
),
delivery_date: yup
.date()
.test("future", "should be in the future", (d) =>
d ? isFuture(d) : true,
),
})
.test("payment", "dates", (d) => {
if (
d.pay_deadline &&
d.refund_deadline &&
isAfter(d.refund_deadline, d.pay_deadline)
) {
return new yup.ValidationError(
"pay deadline should be greater than refund",
"asd",
"payments.pay_deadline",
);
}
return true;
}),
});
export const ProductCreateSchema = yup.object().shape({
product_id: yup.string().ensure().required(),
description: yup.string().required(),
unit: yup.string().ensure().required(),
price: yup
.string()
.required()
.test("amount", "the amount is not valid", currencyWithAmountIsValid),
stock: yup.object({}).optional(),
minimum_age: yup.number().optional().min(0),
});
export const ProductUpdateSchema = yup.object().shape({
description: yup.string().required(),
price: yup
.string()
.required()
.test("amount", "the amount is not valid", currencyWithAmountIsValid),
stock: yup.object({}).optional(),
minimum_age: yup.number().optional().min(0),
});
export const TaxSchema = yup.object().shape({
name: yup.string().required().ensure(),
tax: yup
.string()
.required()
.test("amount", "the amount is not valid", currencyWithAmountIsValid),
});
export const NonInventoryProductSchema = yup.object().shape({
quantity: yup.number().required().positive(),
description: yup.string().required(),
unit: yup.string().ensure().required(),
price: yup
.string()
.required()
.test("amount", "the amount is not valid", currencyWithAmountIsValid),
});
const timestampSchema = yup.object().shape({
t_s: yup.mixed().test(
'is-timestamp',
'Invalid timestamp',
value => typeof value === 'number' || value === 'never'
)
}).required();
const durationSchema = yup.object().shape({
d_us: yup.mixed().test(
'is-duration',
'Invalid duration',
value => typeof value === 'number' || value === 'forever'
)
}).required();
const tokenFamilyKindSchema = yup.mixed().oneOf(["discount", "subscription"]).required();
export const TokenFamilyCreateSchema = yup.object().shape({
slug: yup.string().ensure().required(),
name: yup.string().required(),
description: yup.string().required(),
// description_i18n: yup.lazy((obj) =>
// yup.object().shape(
// Object.keys(obj || {}).reduce((acc, key) => {
// acc[key] = yup.string().required();
// return acc;
// }, {})
// )
// ).optional(),
valid_after: timestampSchema.optional(),
valid_before: timestampSchema,
duration: durationSchema,
kind: tokenFamilyKindSchema,
});
export const TokenFamilyUpdateSchema = yup.object().shape({
name: yup.string().required(),
description: yup.string().required(),
// description_i18n: yup.lazy((obj) =>
// yup.object().shape(
// Object.keys(obj).reduce((acc, key) => {
// acc[key] = yup.string().required();
// return acc;
// }, {})
// )
// ),
valid_after: timestampSchema,
valid_before: timestampSchema,
duration: durationSchema,
});