feat(admin): move allow_update_discount option to product level
All checks were successful
Staging Build / build (push) Successful in 2m50s
All checks were successful
Staging Build / build (push) Successful in 2m50s
This commit is contained in:
@@ -33,7 +33,6 @@ Das Datenbankschema besteht aus folgenden Tabellen im Schema `public`:
|
|||||||
- `show_in_branches` (BOOLEAN)
|
- `show_in_branches` (BOOLEAN)
|
||||||
- `preselect` (BOOLEAN)
|
- `preselect` (BOOLEAN)
|
||||||
- `resets_others` (BOOLEAN)
|
- `resets_others` (BOOLEAN)
|
||||||
- `allow_update_discount` (BOOLEAN) <-- *Berechtigung für Update-Rabatt*
|
|
||||||
|
|
||||||
### `products` (Katalogprodukte / Basis-Editionen)
|
### `products` (Katalogprodukte / Basis-Editionen)
|
||||||
- Hauptlösungen (z.B. Basic-Kasse, Backoffice).
|
- Hauptlösungen (z.B. Basic-Kasse, Backoffice).
|
||||||
@@ -44,6 +43,7 @@ Das Datenbankschema besteht aus folgenden Tabellen im Schema `public`:
|
|||||||
- `tax_rate` (DECIMAL)
|
- `tax_rate` (DECIMAL)
|
||||||
- `billing_interval` (TEXT: `'one_time'` / `'monthly'`)
|
- `billing_interval` (TEXT: `'one_time'` / `'monthly'`)
|
||||||
- `show_in_branches` (BOOLEAN)
|
- `show_in_branches` (BOOLEAN)
|
||||||
|
- `allow_update_discount` (BOOLEAN) <-- *Berechtigung für Update-Rabatt*
|
||||||
|
|
||||||
### `modules` (Zusatzmodule / Erweiterungen)
|
### `modules` (Zusatzmodule / Erweiterungen)
|
||||||
- Optionale Erweiterungen für Produkte. Repräsentiert im TypeScript-Code durch das Interface `ProductModule`.
|
- Optionale Erweiterungen für Produkte. Repräsentiert im TypeScript-Code durch das Interface `ProductModule`.
|
||||||
|
|||||||
@@ -49,7 +49,6 @@ const categorySchema = z.object({
|
|||||||
show_in_kauf: z.boolean().default(true),
|
show_in_kauf: z.boolean().default(true),
|
||||||
show_in_abo: z.boolean().default(true),
|
show_in_abo: z.boolean().default(true),
|
||||||
resets_others: z.boolean().default(false),
|
resets_others: z.boolean().default(false),
|
||||||
allow_update_discount: z.boolean().default(true),
|
|
||||||
})
|
})
|
||||||
|
|
||||||
type CategoryFormValues = z.infer<typeof categorySchema>
|
type CategoryFormValues = z.infer<typeof categorySchema>
|
||||||
@@ -78,7 +77,6 @@ export function CategoryDialog({
|
|||||||
show_in_kauf: category?.show_in_kauf ?? true,
|
show_in_kauf: category?.show_in_kauf ?? true,
|
||||||
show_in_abo: category?.show_in_abo ?? true,
|
show_in_abo: category?.show_in_abo ?? true,
|
||||||
resets_others: category?.resets_others ?? false,
|
resets_others: category?.resets_others ?? false,
|
||||||
allow_update_discount: category?.allow_update_discount ?? true,
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -296,29 +294,6 @@ export function CategoryDialog({
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="allow_update_discount"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem className="flex flex-row items-start space-x-3 space-y-0 rounded-lg border border-slate-200 dark:border-white/10 bg-slate-50 dark:bg-white/5 p-4">
|
|
||||||
<FormControl>
|
|
||||||
<Checkbox
|
|
||||||
checked={field.value}
|
|
||||||
onCheckedChange={field.onChange}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
<div className="space-y-1 leading-none">
|
|
||||||
<FormLabel className="text-slate-900 dark:text-white font-semibold cursor-pointer">
|
|
||||||
Update-Rabatt erlauben
|
|
||||||
</FormLabel>
|
|
||||||
<p className="text-xs text-slate-500 dark:text-slate-400">
|
|
||||||
Wenn aktiv, erhalten Kauf-Lizenzen dieser Kategorie den Update-Rabatt.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div className="grid grid-cols-2 gap-4">
|
<div className="grid grid-cols-2 gap-4">
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ const productSchema = z.object({
|
|||||||
requirements: z.array(z.string()).default([]),
|
requirements: z.array(z.string()).default([]),
|
||||||
exclusions: z.array(z.string()).default([]),
|
exclusions: z.array(z.string()).default([]),
|
||||||
modules: z.array(moduleSchema).default([]),
|
modules: z.array(moduleSchema).default([]),
|
||||||
|
allow_update_discount: z.boolean().default(true),
|
||||||
})
|
})
|
||||||
|
|
||||||
type ProductFormValues = z.infer<typeof productSchema>
|
type ProductFormValues = z.infer<typeof productSchema>
|
||||||
@@ -93,6 +94,7 @@ export function CreateProductDialog({
|
|||||||
show_in_abo: product ? (product.show_in_abo ?? true) : true,
|
show_in_abo: product ? (product.show_in_abo ?? true) : true,
|
||||||
requirements: product?.requirements || [],
|
requirements: product?.requirements || [],
|
||||||
exclusions: product?.exclusions || [],
|
exclusions: product?.exclusions || [],
|
||||||
|
allow_update_discount: product ? (product.allow_update_discount ?? true) : true,
|
||||||
modules: product?.modules?.map(m => ({
|
modules: product?.modules?.map(m => ({
|
||||||
id: m.id,
|
id: m.id,
|
||||||
name: m.name,
|
name: m.name,
|
||||||
@@ -277,6 +279,30 @@ export function CreateProductDialog({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{form.watch('billing_interval') === 'one_time' && (
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="allow_update_discount"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem className="flex flex-row items-start space-x-3 space-y-0 rounded-md border p-4 bg-slate-50 dark:bg-white/5 border-slate-200 dark:border-white/10">
|
||||||
|
<FormControl>
|
||||||
|
<Checkbox
|
||||||
|
checked={field.value}
|
||||||
|
onCheckedChange={field.onChange}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<div className="space-y-1 leading-none">
|
||||||
|
<FormLabel className="text-slate-900 dark:text-white font-medium cursor-pointer">Update-Rabatt erlauben</FormLabel>
|
||||||
|
<FormDescription className="text-xs text-slate-500">
|
||||||
|
Kunden erhalten für dieses Produkt den selektiven Update-Rabatt.
|
||||||
|
</FormDescription>
|
||||||
|
</div>
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="description"
|
name="description"
|
||||||
|
|||||||
@@ -389,7 +389,7 @@ export function OrderWizard({
|
|||||||
})
|
})
|
||||||
const totalItem = base + modulesSum
|
const totalItem = base + modulesSum
|
||||||
if (prod.billing_interval === 'one_time') {
|
if (prod.billing_interval === 'one_time') {
|
||||||
const allowDiscount = (cat as any).allow_update_discount !== false
|
const allowDiscount = (prod as any).allow_update_discount !== false
|
||||||
const finalItemTotal = allowDiscount ? (totalItem * updatePriceModifier.multiplier) : totalItem
|
const finalItemTotal = allowDiscount ? (totalItem * updatePriceModifier.multiplier) : totalItem
|
||||||
oneTime += finalItemTotal
|
oneTime += finalItemTotal
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -183,11 +183,11 @@ export function buildOrderSnapshot(
|
|||||||
multiplier = 0.90
|
multiplier = 0.90
|
||||||
}
|
}
|
||||||
|
|
||||||
// Multiply only one_time item totals by multiplier if category allows it
|
// Multiply only one_time item totals by multiplier if product allows it
|
||||||
items.forEach(item => {
|
items.forEach(item => {
|
||||||
if (item.billing_interval === 'one_time') {
|
if (item.billing_interval === 'one_time') {
|
||||||
const cat = categories.find(c => c.id === item.category_id);
|
const prod = products.find(p => p.id === item.product_id);
|
||||||
const allowDiscount = cat ? (cat as any).allow_update_discount !== false : true;
|
const allowDiscount = prod ? prod.allow_update_discount !== false : true;
|
||||||
if (allowDiscount) {
|
if (allowDiscount) {
|
||||||
item.item_total = item.item_total * multiplier;
|
item.item_total = item.item_total * multiplier;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ export type Product = {
|
|||||||
show_in_abo?: boolean
|
show_in_abo?: boolean
|
||||||
requirements?: string[]
|
requirements?: string[]
|
||||||
exclusions?: string[]
|
exclusions?: string[]
|
||||||
|
allow_update_discount?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Category = {
|
export type Category = {
|
||||||
@@ -32,7 +33,6 @@ export type Category = {
|
|||||||
show_in_kauf?: boolean
|
show_in_kauf?: boolean
|
||||||
show_in_abo?: boolean
|
show_in_abo?: boolean
|
||||||
resets_others: boolean
|
resets_others: boolean
|
||||||
allow_update_discount?: boolean
|
|
||||||
created_at: string
|
created_at: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
-- Migration: Move allow_update_discount from categories to products
|
||||||
|
-- Purpose: Control update discount eligibility on product level rather than category level.
|
||||||
|
|
||||||
|
ALTER TABLE public.categories DROP COLUMN IF EXISTS allow_update_discount;
|
||||||
|
ALTER TABLE public.products ADD COLUMN IF NOT EXISTS allow_update_discount BOOLEAN DEFAULT true NOT NULL;
|
||||||
|
|
||||||
|
NOTIFY pgrst, 'reload schema';
|
||||||
Reference in New Issue
Block a user