- {items.map((item, i) => (
+ {items.map((item, idx) => (
))}
diff --git a/shop/app/order/success/page.tsx b/shop/app/order/success/page.tsx
index c8693a3..d1be63a 100644
--- a/shop/app/order/success/page.tsx
+++ b/shop/app/order/success/page.tsx
@@ -118,7 +118,7 @@ export default async function OrderSuccessPage({
{Object.entries(groupedItems).map(([deviceName, devItems]) => (
diff --git a/shop/components/wizard/step-customer.tsx b/shop/components/wizard/step-customer.tsx
index 396dd75..22b0c9a 100644
--- a/shop/components/wizard/step-customer.tsx
+++ b/shop/components/wizard/step-customer.tsx
@@ -177,7 +177,7 @@ export function StepCustomer({
setPartnerSearchTerm(e.target.value)}
className="pl-9 bg-white/5 border-white/10 text-white placeholder:text-slate-500 focus:border-primary text-sm h-9"
@@ -202,7 +202,10 @@ export function StepCustomer({
{/* Reserved Fixed Card: Alle Partner */}
setSelectedCompanyId('all')}
+ onClick={() => {
+ setSelectedCompanyId('all')
+ setSelectedEndCustomerId(null)
+ }}
className={`flex items-center justify-between p-3.5 rounded-xl border-2 cursor-pointer transition-all select-none ${
selectedCompanyId === 'all' || !selectedCompanyId
? 'border-primary bg-primary/10 shadow-lg shadow-primary/5'
@@ -240,7 +243,10 @@ export function StepCustomer({
return (
setSelectedCompanyId(c.id)}
+ onClick={() => {
+ setSelectedCompanyId(c.id)
+ setSelectedEndCustomerId(null)
+ }}
className={`flex items-center justify-between p-3.5 rounded-xl border-2 cursor-pointer transition-all select-none ${
isSelected
? 'border-primary bg-primary/10 shadow-lg shadow-primary/5'
@@ -253,7 +259,11 @@ export function StepCustomer({
{c.name}
-
ID: {c.id}
+ {(c.street || c.zip || c.city) && (
+
+ {[c.street, [c.zip, c.city].filter(Boolean).join(' ')].filter(Boolean).join(', ')}
+
+ )}
@@ -592,9 +602,7 @@ export function StepCustomer({
onClick={nextStep}
disabled={
customerMode === 'create' ||
- (customerMode === 'select' &&
- customersByCompany.length > 0 &&
- !selectedEndCustomerId)
+ !selectedEndCustomerId
}
>
Weiter zum Abrechnungsmodell
diff --git a/shop/components/wizard/step-summary.tsx b/shop/components/wizard/step-summary.tsx
index b73eeee..f28aa22 100644
--- a/shop/components/wizard/step-summary.tsx
+++ b/shop/components/wizard/step-summary.tsx
@@ -28,6 +28,7 @@ interface StepSummaryProps {
isSubmitting: boolean
initialOrder: any
prevStep: () => void
+ linkedFeeProducts?: Product[]
}
function CategoryIcon({ icon, className }: { icon?: string | null; className?: string }) {
@@ -56,6 +57,7 @@ export function StepSummary({
isSubmitting,
initialOrder,
prevStep,
+ linkedFeeProducts = [],
}: StepSummaryProps) {
return (
@@ -142,6 +144,24 @@ export function StepSummary({
})}
))}
+ {linkedFeeProducts && linkedFeeProducts.length > 0 && (
+
+
+ Backoffice
+
+ {linkedFeeProducts.map(p => (
+
+
+ {p.name}
+
+ {new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(p.base_price)}
+ {' '}{p.billing_interval === 'monthly' ? '/ mtl.' : 'einmalig'}
+
+
+
+ ))}
+
+ )}
{selectedEndCustomer ? (
diff --git a/shop/lib/types.ts b/shop/lib/types.ts
index b47ea04..8d431f9 100644
--- a/shop/lib/types.ts
+++ b/shop/lib/types.ts
@@ -18,6 +18,7 @@ export type Product = {
requirements?: string[]
exclusions?: string[]
allow_update_discount?: boolean
+ linked_fee_product_id?: string | null
}
export type Category = {
diff --git a/shop/supabase/migrations/20260722100000_add_linked_fee_to_modules.sql b/shop/supabase/migrations/20260722100000_add_linked_fee_to_modules.sql
new file mode 100644
index 0000000..331de7d
--- /dev/null
+++ b/shop/supabase/migrations/20260722100000_add_linked_fee_to_modules.sql
@@ -0,0 +1,3 @@
+-- Add linked_fee_product_id to product_modules table
+ALTER TABLE public.product_modules
+ADD COLUMN IF NOT EXISTS linked_fee_product_id UUID REFERENCES public.products(id) ON DELETE SET NULL;
diff --git a/shop/supabase/migrations/20260722110000_move_linked_fee_to_products.sql b/shop/supabase/migrations/20260722110000_move_linked_fee_to_products.sql
new file mode 100644
index 0000000..212a29a
--- /dev/null
+++ b/shop/supabase/migrations/20260722110000_move_linked_fee_to_products.sql
@@ -0,0 +1,7 @@
+-- Remove linked_fee_product_id from product_modules
+ALTER TABLE public.product_modules
+DROP COLUMN IF EXISTS linked_fee_product_id;
+
+-- Add linked_fee_product_id to products
+ALTER TABLE public.products
+ADD COLUMN IF NOT EXISTS linked_fee_product_id UUID REFERENCES public.products(id) ON DELETE SET NULL;
diff --git a/shop/supabase/seed.sql b/shop/supabase/seed.sql
index 5637f91..42519f9 100644
--- a/shop/supabase/seed.sql
+++ b/shop/supabase/seed.sql
@@ -1,13 +1,16 @@
-- Seed Products
-INSERT INTO public.products (id, name, description, base_price, tax_rate)
+INSERT INTO public.products (id, name, description, base_price, tax_rate, billing_interval, show_in_abo, show_in_kauf)
VALUES
-('d1a1a1a1-a1a1-a1a1-a1a1-a1a1a1a1a1a1', 'CASPOS Cloud', 'Die modulare Cloud-Lösung für Ihren Einzelhandel.', 49.00, 19.00),
-('d2a2a2a2-a2a2-a2a2-a2a2-a2a2a2a2a2a2', 'CASPOS Gastro', 'Spezialisiert auf Gastronomie mit Tischplan und Funkbonieren.', 79.00, 19.00);
+('d1a1a1a1-a1a1-a1a1-a1a1-a1a1a1a1a1a1', 'CASPOS Cloud', 'Die modulare Cloud-Lösung für Ihren Einzelhandel.', 49.00, 19.00, 'monthly', true, true),
+('d2a2a2a2-a2a2-a2a2-a2a2-a2a2-a2a2-a2a2a2a2', 'CASPOS Gastro', 'Spezialisiert auf Gastronomie mit Tischplan und Funkbonieren.', 79.00, 19.00, 'monthly', true, true),
+('prod-poscloud-fee', 'POS Cloud Grundgebühr', 'Monatliche Grundgebühr für die POS Cloud Nutzung.', 19.00, 19.00, 'monthly', false, false);
-- Seed Modules for CASPOS Cloud
INSERT INTO public.product_modules (id, product_id, name, description, price, requirements, exclusions)
VALUES
('m1a1a1a1-a1a1-a1a1-a1a1-a1a1a1a1a1a1', 'd1a1a1a1-a1a1-a1a1-a1a1-a1a1a1a1a1a1', 'Bestandsführung Pro', 'Erweiterte Lagerverwaltung.', 15.00, '{}', '{}'),
-('m2a2a2a2-a2a2-a2a2-a2a2-a2a2a2a2a2a2', 'd1a1a1a1-a1a1-a1a1-a1a1-a1a1a1a1a1a1', 'DATEV Export', 'Direkte Schnittstelle zum Steuerberater.', 10.00, '{}', '{}'),
-('m3a3a3a3-a3a3-a3a3-a3a3-a3a3a3a3a3a3', 'd1a1a1a1-a1a1-a1a1-a1a1-a1a1a1a1a1a1', 'Filialverwaltung', 'Zentrale Steuerung mehrerer Standorte.', 25.00, '{"m1a1a1a1-a1a1-a1a1-a1a1-a1a1a1a1a1a1"}', '{}'),
-('m4a4a4a4-a4a4-a4a4-a4a4-a4a4a4a4a4a4', 'd1a1a1a1-a1a1-a1a1-a1a1-a1a1a1a1a1a1', 'Small Business Modus', 'Reduzierter Funktionsumfang für Kleinunternehmer.', 0.00, '{}', '{"m1a1a1a1-a1a1-a1a1-a1a1-a1a1a1a1a1a1", "m3a3a3a3-a3a3-a3a3-a3a3-a3a3a3a3a3a3"}');
+('m2a2a2a2-a2a2-a2a2-a2a2-a2a2-a2a2-a2a2a2a2', 'd1a1a1a1-a1a1-a1a1-a1a1-a1a1a1a1a1a1', 'DATEV Export', 'Direkte Schnittstelle zum Steuerberater.', 10.00, '{}', '{}'),
+('m3a3a3a3-a3a3-a3a3-a3a3-a3a3-a3a3-a3a3-a3a3', 'd1a1a1a1-a1a1-a1a1-a1a1-a1a1a1a1a1a1', 'Filialverwaltung', 'Zentrale Steuerung mehrerer Standorte.', 25.00, '{"m1a1a1a1-a1a1-a1a1-a1a1-a1a1a1a1a1a1"}', '{}'),
+('m4a4a4a4-a4a4-a4a4-a4a4-a4a4-a4a4-a4a4-a4a4', 'd1a1a1a1-a1a1-a1a1-a1a1-a1a1a1a1a1a1', 'Small Business Modus', 'Reduzierter Funktionsumfang für Kleinunternehmer.', 0.00, '{}', '{"m1a1a1a1-a1a1-a1a1-a1a1-a1a1a1a1a1a1", "m3a3a3a3-a3a3-a3a3-a3a3-a3a3-a3a3-a3a3-a3a3"}'),
+('m-poscloud-cloud', 'd1a1a1a1-a1a1-a1a1-a1a1-a1a1a1a1a1a1', 'Schnittstelle POS Cloud', 'Anbindung an die POS Cloud.', 0.00, '{}', '{}'),
+('m-poscloud-gastro', 'd2a2a2a2-a2a2-a2a2-a2a2-a2a2-a2a2-a2a2a2a2', 'Schnittstelle POS Cloud', 'Anbindung an die POS Cloud.', 0.00, '{}', '{}');