chore(merge): integrate origin/staging into local staging
All checks were successful
Staging Build / build (push) Successful in 2m57s
All checks were successful
Staging Build / build (push) Successful in 2m57s
Resolved conflict in customer-accordion-list.tsx by keeping the per-device flattening (DeviceCard) implementation.
This commit is contained in:
@@ -402,6 +402,30 @@ export function WysiwygAdminClient({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Verknüpfte monatliche Gebühr */}
|
||||
<div className="space-y-1 mt-2">
|
||||
<span className="text-[10px] uppercase tracking-wider text-slate-500 font-semibold block">Verknüpfte Gebühr:</span>
|
||||
<select
|
||||
value={prod.linked_fee_product_id || ''}
|
||||
onChange={async (e) => {
|
||||
const val = e.target.value || null
|
||||
setProducts(products.map(p => p.id === prod.id ? { ...p, linked_fee_product_id: val } : p))
|
||||
await updateProduct(prod.id, { linked_fee_product_id: val }, prod.modules || [])
|
||||
}}
|
||||
className="w-full bg-slate-950 border border-white/10 text-[10px] text-slate-300 focus:ring-0 cursor-pointer rounded px-2 py-1 outline-none"
|
||||
>
|
||||
<option value="">Keine Gebühr verknüpft</option>
|
||||
{products
|
||||
.filter(p => p.billing_interval === 'monthly' && p.id !== prod.id)
|
||||
.map(p => (
|
||||
<option key={p.id} value={p.id}>
|
||||
{p.name} ({new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(p.base_price)} / mtl.)
|
||||
</option>
|
||||
))
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{/* Modul hinzufügen Popover */}
|
||||
<div className="mt-3 pt-2 border-t border-white/5">
|
||||
<ModulePopover
|
||||
|
||||
@@ -82,11 +82,9 @@ export async function GET(
|
||||
|
||||
const buffer = await renderToBuffer(
|
||||
React.createElement(InvoicePDF, {
|
||||
orderNumber: order.order_number || `AE-${id.slice(0, 8)}`,
|
||||
dateStr: formattedDate,
|
||||
order: order,
|
||||
orderSnapshot: order.order_data,
|
||||
customer: order.customer_data,
|
||||
orderData: order.order_data,
|
||||
totalPrice: Number(order.total_price),
|
||||
})
|
||||
);
|
||||
|
||||
|
||||
@@ -82,11 +82,9 @@ export async function GET(
|
||||
|
||||
const buffer = await renderToBuffer(
|
||||
React.createElement(InvoicePDF, {
|
||||
orderNumber: order.order_number || `AE-${id.slice(0, 8)}`,
|
||||
dateStr: formattedDate,
|
||||
order: order,
|
||||
orderSnapshot: order.order_data,
|
||||
customer: order.customer_data,
|
||||
orderData: order.order_data,
|
||||
totalPrice: Number(order.total_price),
|
||||
})
|
||||
);
|
||||
|
||||
|
||||
@@ -77,6 +77,7 @@ export async function POST(request: Request) {
|
||||
|
||||
const orderItemsList: any[] = [];
|
||||
let total = 0;
|
||||
const feeProductIds = new Set<string>();
|
||||
|
||||
for (const item of groupItems) {
|
||||
const itemSnapshot = buildOrderSnapshot(
|
||||
@@ -93,8 +94,53 @@ export async function POST(request: Request) {
|
||||
}));
|
||||
orderItemsList.push(...itemsWithDevice);
|
||||
total += itemSnapshot.total;
|
||||
|
||||
// Echte linked fee products ermitteln
|
||||
if (item.selections) {
|
||||
for (const catId in item.selections) {
|
||||
const sel = item.selections[catId];
|
||||
|
||||
if (sel.productId) {
|
||||
const p = products.find((prod: any) => prod.id === sel.productId);
|
||||
if (p && p.linked_fee_product_id) {
|
||||
feeProductIds.add(p.linked_fee_product_id);
|
||||
}
|
||||
}
|
||||
|
||||
sel.productIds?.forEach((pId: string) => {
|
||||
const p = products.find((prod: any) => prod.id === pId);
|
||||
if (p && p.linked_fee_product_id) {
|
||||
feeProductIds.add(p.linked_fee_product_id);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
feeProductIds.forEach(feeProdId => {
|
||||
const feeProduct = products.find((p: any) => p.id === feeProdId);
|
||||
if (feeProduct) {
|
||||
const isIntervalMatch =
|
||||
(type === 'purchase' && feeProduct.billing_interval === 'one_time') ||
|
||||
(type === 'subscription' && feeProduct.billing_interval === 'monthly');
|
||||
|
||||
if (isIntervalMatch) {
|
||||
orderItemsList.push({
|
||||
category_id: 'dienste-gebuehren',
|
||||
category_name: 'Dienste & Gebühren',
|
||||
product_id: feeProduct.id,
|
||||
product_name: feeProduct.name,
|
||||
base_price: Number(feeProduct.base_price),
|
||||
billing_interval: feeProduct.billing_interval,
|
||||
selected_modules: [],
|
||||
item_total: Number(feeProduct.base_price),
|
||||
device_name: 'Zusatzleistung'
|
||||
});
|
||||
total += Number(feeProduct.base_price);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const orderSnapshot = {
|
||||
schema_version: 1,
|
||||
billing_cycle: type === 'purchase' ? 'one_time' : 'monthly',
|
||||
|
||||
@@ -126,14 +126,17 @@ export default async function MyOrdersPage() {
|
||||
{/* Produkte kurz */}
|
||||
{items.length > 0 && (
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{items.map((item, i) => (
|
||||
{items.map((item, idx) => (
|
||||
<span
|
||||
key={item.product_id}
|
||||
className="text-xs bg-white/5 border border-white/10 rounded-full px-3 py-1 text-slate-300"
|
||||
key={idx}
|
||||
className="text-xs bg-white/5 border border-white/10 rounded-xl px-3 py-1.5 text-slate-300"
|
||||
>
|
||||
<span className="font-semibold text-white mr-1">{item.device_name || 'Kasse'}:</span>
|
||||
{item.product_name}
|
||||
{item.selected_modules.length > 0 && (
|
||||
<span className="text-slate-500 ml-1">+{item.selected_modules.length} Module</span>
|
||||
<span className="text-slate-500 ml-1">
|
||||
(+{item.selected_modules.length} {item.selected_modules.length === 1 ? 'Modul' : 'Module'})
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
))}
|
||||
|
||||
@@ -118,7 +118,7 @@ export default async function OrderSuccessPage({
|
||||
{Object.entries(groupedItems).map(([deviceName, devItems]) => (
|
||||
<div key={deviceName} className="space-y-2 border-b border-white/5 pb-3 last:border-0 last:pb-0">
|
||||
<div className="bg-white/5 px-2 py-1 rounded text-xs text-primary font-bold">
|
||||
Kasse: {deviceName}
|
||||
{deviceName === 'Zusatzleistung' ? 'Backoffice' : `Kasse: ${deviceName}`}
|
||||
</div>
|
||||
{devItems.map((item) => (
|
||||
<div key={item.product_id} className="space-y-1 pl-2">
|
||||
|
||||
Reference in New Issue
Block a user