Compare commits
No commits in common. "94dac61941505e29dfc07973ab7d5d3238ce0242" and "2e5af647364841a91c0a1308448623b2e83160e6" have entirely different histories.
94dac61941
...
2e5af64736
@ -24,7 +24,6 @@ export default function ContractEditor({ onSaved, editingTemplateId, onCancelEdi
|
||||
const [type, setType] = useState<'contract' | 'invoice' | 'other'>('contract');
|
||||
const [contractType, setContractType] = useState<'contract' | 'gdpr' | 'abo'>('contract');
|
||||
const [userType, setUserType] = useState<'personal' | 'company' | 'both'>('personal');
|
||||
const [taxMode, setTaxMode] = useState<'standard' | 'reverse_charge' | 'both'>('both');
|
||||
const [description, setDescription] = useState<string>('');
|
||||
|
||||
const [editingMeta, setEditingMeta] = useState<{ id: string; version: number; state: string } | null>(null);
|
||||
@ -39,7 +38,6 @@ export default function ContractEditor({ onSaved, editingTemplateId, onCancelEdi
|
||||
setName('');
|
||||
setHtmlCode('');
|
||||
setDescription('');
|
||||
setTaxMode('both');
|
||||
setIsPreview(false);
|
||||
setEditingMeta(null);
|
||||
};
|
||||
@ -62,7 +60,6 @@ export default function ContractEditor({ onSaved, editingTemplateId, onCancelEdi
|
||||
setType(((tpl.type as any) || 'contract') as 'contract' | 'invoice' | 'other');
|
||||
setContractType(((tpl.contract_type as any) || 'contract') as 'contract' | 'gdpr' | 'abo');
|
||||
setUserType(((tpl.user_type as any) || 'both') as 'personal' | 'company' | 'both');
|
||||
setTaxMode(((tpl.tax_mode as any) || 'both') as 'standard' | 'reverse_charge' | 'both');
|
||||
setEditingMeta({
|
||||
id: editingTemplateId,
|
||||
version: Number(tpl.version || 1),
|
||||
@ -178,7 +175,6 @@ export default function ContractEditor({ onSaved, editingTemplateId, onCancelEdi
|
||||
contract_type: type === 'contract' ? contractType : null,
|
||||
lang,
|
||||
user_type: userType,
|
||||
tax_mode: type === 'invoice' ? taxMode : null,
|
||||
descriptionLength: description ? description.length : 0,
|
||||
htmlLength: html.length,
|
||||
});
|
||||
@ -197,7 +193,6 @@ export default function ContractEditor({ onSaved, editingTemplateId, onCancelEdi
|
||||
name,
|
||||
type,
|
||||
contract_type: type === 'contract' ? contractType : undefined,
|
||||
tax_mode: type === 'invoice' ? taxMode : undefined,
|
||||
lang,
|
||||
description: description || undefined,
|
||||
user_type: userType,
|
||||
@ -215,7 +210,6 @@ export default function ContractEditor({ onSaved, editingTemplateId, onCancelEdi
|
||||
name,
|
||||
type,
|
||||
contract_type: type === 'contract' ? contractType : undefined,
|
||||
tax_mode: type === 'invoice' ? taxMode : undefined,
|
||||
lang,
|
||||
description: description || undefined,
|
||||
user_type: userType,
|
||||
@ -243,10 +237,7 @@ export default function ContractEditor({ onSaved, editingTemplateId, onCancelEdi
|
||||
: type === 'invoice'
|
||||
? 'Invoice'
|
||||
: 'Other';
|
||||
const scope = type === 'invoice'
|
||||
? 'same user type, tax mode and language'
|
||||
: 'same user type and language';
|
||||
setPublishConfirmMessage(`This will deactivate other active ${kind} templates that apply to the ${scope}.`)
|
||||
setPublishConfirmMessage(`This will deactivate other active ${kind} templates that apply to the same user type and language.`)
|
||||
setPublishConfirmOpen(true)
|
||||
return
|
||||
}
|
||||
@ -332,18 +323,6 @@ export default function ContractEditor({ onSaved, editingTemplateId, onCancelEdi
|
||||
<option value="abo">ABO</option>
|
||||
</select>
|
||||
)}
|
||||
{type === 'invoice' && (
|
||||
<select
|
||||
value={taxMode}
|
||||
onChange={(e) => setTaxMode(e.target.value as 'standard' | 'reverse_charge' | 'both')}
|
||||
required
|
||||
className="w-full sm:w-56 rounded-lg border border-gray-300 bg-white px-4 py-2 text-sm text-gray-900 outline-none focus:ring-2 focus:ring-indigo-500/50 shadow"
|
||||
>
|
||||
<option value="both">Invoice mode: Both / Fallback</option>
|
||||
<option value="standard">Invoice mode: Standard VAT</option>
|
||||
<option value="reverse_charge">Invoice mode: Reverse Charge</option>
|
||||
</select>
|
||||
)}
|
||||
<select
|
||||
value={userType}
|
||||
onChange={(e) => setUserType(e.target.value as 'personal' | 'company' | 'both')}
|
||||
@ -379,7 +358,6 @@ export default function ContractEditor({ onSaved, editingTemplateId, onCancelEdi
|
||||
<div className="rounded-lg border border-amber-200 bg-amber-50 px-4 py-3 text-xs text-amber-900">
|
||||
<p className="font-semibold">{t('autofix.k221fa311')}</p>
|
||||
<p className="mt-1">{t('autofix.kb791958e')}</p>
|
||||
<p className="mt-1">Use the invoice mode dropdown to separate standard VAT and reverse-charge company templates. Keep "Both / Fallback" only for a shared default layout.</p>
|
||||
<p className="mt-1">Important: include <span className="font-semibold">itemsHtml</span>{t('autofix.k7a3a6ea3')}</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@ -19,7 +19,6 @@ type ContractTemplate = {
|
||||
type?: string;
|
||||
contract_type?: string | null;
|
||||
user_type?: string | null;
|
||||
tax_mode?: string | null;
|
||||
lang?: string | null;
|
||||
version: number;
|
||||
status: 'draft' | 'published' | 'archived' | string;
|
||||
@ -30,7 +29,6 @@ type ContractTemplate = {
|
||||
type TemplateFamilyKey = 'contract' | 'invoice' | 'other';
|
||||
type ContractTypeKey = 'contract' | 'gdpr' | 'abo';
|
||||
type AudienceKey = 'personal' | 'company' | 'both';
|
||||
type InvoiceTaxModeKey = 'standard' | 'reverse_charge' | 'both';
|
||||
|
||||
type TemplateLanguageColumn = {
|
||||
key: string;
|
||||
@ -93,7 +91,6 @@ type NormalizedTemplate = DocumentTemplate & {
|
||||
updated_at?: string;
|
||||
contractType?: string | null;
|
||||
userType?: string | null;
|
||||
taxMode?: string | null;
|
||||
};
|
||||
|
||||
const FAMILY_ORDER: TemplateFamilyKey[] = ['contract', 'invoice', 'other'];
|
||||
@ -178,11 +175,6 @@ function normalizeUserType(userType?: string | null): AudienceKey {
|
||||
return ['personal', 'company', 'both'].includes(value) ? (value as AudienceKey) : 'both';
|
||||
}
|
||||
|
||||
function normalizeInvoiceTaxMode(taxMode?: string | null): InvoiceTaxModeKey {
|
||||
const value = (taxMode || 'both').toLowerCase();
|
||||
return ['standard', 'reverse_charge', 'both'].includes(value) ? (value as InvoiceTaxModeKey) : 'both';
|
||||
}
|
||||
|
||||
function normalizeLanguage(lang?: string | null) {
|
||||
const value = (lang || '').toLowerCase();
|
||||
return value === 'de' || value === 'en' ? value : 'other';
|
||||
@ -225,17 +217,6 @@ function formatLanguageCode(lang?: string | null) {
|
||||
}
|
||||
}
|
||||
|
||||
function formatInvoiceTaxMode(taxMode?: string | null) {
|
||||
switch (normalizeInvoiceTaxMode(taxMode)) {
|
||||
case 'standard':
|
||||
return 'Standard VAT';
|
||||
case 'reverse_charge':
|
||||
return 'Reverse Charge';
|
||||
default:
|
||||
return 'Fallback / Both';
|
||||
}
|
||||
}
|
||||
|
||||
function formatContractType(contractType?: string | null) {
|
||||
switch (normalizeContractType(contractType)) {
|
||||
case 'abo':
|
||||
@ -291,7 +272,6 @@ function buildTrackKey(template: ContractTemplate) {
|
||||
family,
|
||||
normalizeContractType(template.contract_type),
|
||||
normalizeUserType(template.user_type),
|
||||
family === 'invoice' ? normalizeInvoiceTaxMode(template.tax_mode) : 'default',
|
||||
normalizeName(template.name),
|
||||
].join(':');
|
||||
}
|
||||
@ -330,7 +310,7 @@ function buildTrack(templates: ContractTemplate[]): TemplateTrack {
|
||||
|
||||
if (family === 'invoice') {
|
||||
title = names.length === 1 ? names[0] : 'Invoice Templates';
|
||||
subtitle = `${formatUserType(lead?.user_type)} • ${formatInvoiceTaxMode(lead?.tax_mode)} • ${sortedTemplates.length} version${sortedTemplates.length === 1 ? '' : 's'}`;
|
||||
subtitle = `${formatUserType(lead?.user_type)} • ${sortedTemplates.length} version${sortedTemplates.length === 1 ? '' : 's'}`;
|
||||
}
|
||||
|
||||
return {
|
||||
@ -540,13 +520,11 @@ export default function ContractTemplateList({ refreshKey = 0, onEdit }: Props)
|
||||
item.type || '',
|
||||
item.contract_type || '',
|
||||
item.user_type || '',
|
||||
item.tax_mode || '',
|
||||
item.lang || '',
|
||||
item.status,
|
||||
`v${item.version}`,
|
||||
formatContractType(item.contract_type),
|
||||
formatUserType(item.user_type),
|
||||
formatInvoiceTaxMode(item.tax_mode),
|
||||
formatLanguage(item.lang),
|
||||
]
|
||||
.join(' ')
|
||||
@ -636,7 +614,6 @@ export default function ContractTemplateList({ refreshKey = 0, onEdit }: Props)
|
||||
type: normalized.type,
|
||||
contract_type: normalized.contract_type ?? normalized.contractType ?? null,
|
||||
user_type: normalized.user_type ?? normalized.userType ?? null,
|
||||
tax_mode: normalized.tax_mode ?? normalized.taxMode ?? null,
|
||||
lang: normalized.lang ?? null,
|
||||
version: Number(normalized.version ?? 1),
|
||||
status: normalized.state === 'active' ? 'published' : 'draft',
|
||||
@ -650,7 +627,7 @@ export default function ContractTemplateList({ refreshKey = 0, onEdit }: Props)
|
||||
setItems((prev) => prev.length ? prev : [
|
||||
{ id: 'ex1', name: 'Standard Subscription Agreement', type: 'contract', contract_type: 'abo', user_type: 'personal', lang: 'de', version: 2, status: 'published', updatedAt: new Date().toISOString() },
|
||||
{ id: 'ex2', name: 'Standard Subscription Agreement', type: 'contract', contract_type: 'abo', user_type: 'personal', lang: 'de', version: 1, status: 'draft', updatedAt: new Date().toISOString() },
|
||||
{ id: 'ex3', name: 'Invoice Standard', type: 'invoice', user_type: 'both', tax_mode: 'both', lang: 'en', version: 3, status: 'published', updatedAt: new Date().toISOString() },
|
||||
{ id: 'ex3', name: 'Invoice Standard', type: 'invoice', user_type: 'both', lang: 'en', version: 3, status: 'published', updatedAt: new Date().toISOString() },
|
||||
]);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
@ -688,14 +665,11 @@ export default function ContractTemplateList({ refreshKey = 0, onEdit }: Props)
|
||||
: tpl.type === 'invoice'
|
||||
? 'Invoice'
|
||||
: 'Other';
|
||||
const scope = tpl.type === 'invoice'
|
||||
? 'same user type, tax mode and language'
|
||||
: 'same user type and language';
|
||||
setPendingToggle({
|
||||
id,
|
||||
target,
|
||||
requiresConfirm: true,
|
||||
message: `This will deactivate other active ${kind} templates that apply to the ${scope}.`,
|
||||
message: `This will deactivate other active ${kind} templates that apply to the same user type and language.`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
@ -760,9 +734,6 @@ export default function ContractTemplateList({ refreshKey = 0, onEdit }: Props)
|
||||
{track.family === 'invoice' && track.templates[0]?.user_type && (
|
||||
<Pill className="border-sky-200 bg-white/90 text-sky-800">{formatUserType(track.templates[0].user_type)}</Pill>
|
||||
)}
|
||||
{track.family === 'invoice' && (
|
||||
<Pill className="border-sky-200 bg-sky-100 text-sky-900">{formatInvoiceTaxMode(track.templates[0]?.tax_mode)}</Pill>
|
||||
)}
|
||||
{track.family === 'other' && track.templates[0]?.user_type && (
|
||||
<Pill className="border-slate-200 bg-slate-100 text-slate-700">{formatUserType(track.templates[0].user_type)}</Pill>
|
||||
)}
|
||||
@ -991,7 +962,7 @@ export default function ContractTemplateList({ refreshKey = 0, onEdit }: Props)
|
||||
|
||||
{activeFamily.key === 'invoice' && (
|
||||
<div className="mt-4 rounded-2xl border border-sky-200 bg-sky-50 px-4 py-3 text-sm text-sky-900">
|
||||
Invoice templates can target Personal, Company, or Both. Keep one active version per language, user type and invoice mode so the invoice flow stays predictable.
|
||||
Invoice templates can target Personal, Company, or Both. Keep one active version per language and user type so the invoice flow stays predictable.
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
@ -8,7 +8,6 @@ export type DocumentTemplate = {
|
||||
contract_type?: 'contract' | 'gdpr' | 'abo' | null | string;
|
||||
lang?: 'en' | 'de' | string;
|
||||
user_type?: 'personal' | 'company' | 'both' | string;
|
||||
tax_mode?: 'standard' | 'reverse_charge' | 'both' | null | string;
|
||||
state?: 'active' | 'inactive' | string;
|
||||
version?: number;
|
||||
previewUrl?: string | null;
|
||||
@ -227,7 +226,6 @@ export default function useContractManagement() {
|
||||
lang: 'en' | 'de' | string;
|
||||
description?: string;
|
||||
user_type?: 'personal' | 'company' | 'both';
|
||||
tax_mode?: 'standard' | 'reverse_charge' | 'both';
|
||||
}): Promise<DocumentTemplate> => {
|
||||
const fd = new FormData();
|
||||
const file = payload.file instanceof File ? payload.file : new File([payload.file], `${payload.name || 'template'}.html`, { type: 'text/html' });
|
||||
@ -237,9 +235,6 @@ export default function useContractManagement() {
|
||||
if (payload.type === 'contract' && payload.contract_type) {
|
||||
fd.append('contract_type', payload.contract_type);
|
||||
}
|
||||
if (payload.type === 'invoice' && payload.tax_mode) {
|
||||
fd.append('tax_mode', payload.tax_mode);
|
||||
}
|
||||
fd.append('lang', payload.lang);
|
||||
if (payload.description) fd.append('description', payload.description);
|
||||
fd.append('user_type', (payload.user_type ?? 'both'));
|
||||
@ -252,7 +247,6 @@ export default function useContractManagement() {
|
||||
willSendContractType: payload.type === 'contract' && Boolean(payload.contract_type),
|
||||
lang: payload.lang,
|
||||
user_type: payload.user_type ?? 'both',
|
||||
tax_mode: payload.type === 'invoice' ? (payload.tax_mode ?? 'both') : null,
|
||||
descriptionLength: payload.description ? payload.description.length : 0,
|
||||
file: typeof File !== 'undefined' && file instanceof File ? { name: file.name, type: file.type, size: file.size } : null,
|
||||
});
|
||||
@ -269,7 +263,6 @@ export default function useContractManagement() {
|
||||
lang?: 'en' | 'de' | string;
|
||||
description?: string;
|
||||
user_type?: 'personal' | 'company' | 'both';
|
||||
tax_mode?: 'standard' | 'reverse_charge' | 'both';
|
||||
}): Promise<DocumentTemplate> => {
|
||||
const fd = new FormData();
|
||||
if (payload.file) {
|
||||
@ -281,9 +274,6 @@ export default function useContractManagement() {
|
||||
if ((payload.type === 'contract' || payload.contract_type) && payload.contract_type) {
|
||||
fd.append('contract_type', payload.contract_type);
|
||||
}
|
||||
if ((payload.type === 'invoice' || payload.tax_mode) && payload.tax_mode) {
|
||||
fd.append('tax_mode', payload.tax_mode);
|
||||
}
|
||||
if (payload.lang) fd.append('lang', payload.lang);
|
||||
if (payload.description !== undefined) fd.append('description', payload.description);
|
||||
if (payload.user_type) fd.append('user_type', payload.user_type);
|
||||
@ -300,7 +290,6 @@ export default function useContractManagement() {
|
||||
lang?: 'en' | 'de' | string;
|
||||
description?: string;
|
||||
user_type?: 'personal' | 'company' | 'both';
|
||||
tax_mode?: 'standard' | 'reverse_charge' | 'both';
|
||||
state?: 'active' | 'inactive';
|
||||
}): Promise<DocumentTemplate> => {
|
||||
const fd = new FormData();
|
||||
@ -311,7 +300,6 @@ export default function useContractManagement() {
|
||||
if (payload.name !== undefined) fd.append('name', payload.name);
|
||||
if (payload.type !== undefined) fd.append('type', payload.type);
|
||||
if (payload.contract_type !== undefined) fd.append('contract_type', payload.contract_type);
|
||||
if (payload.tax_mode !== undefined) fd.append('tax_mode', payload.tax_mode);
|
||||
if (payload.lang !== undefined) fd.append('lang', payload.lang);
|
||||
if (payload.description !== undefined) fd.append('description', payload.description);
|
||||
if (payload.user_type !== undefined) fd.append('user_type', payload.user_type);
|
||||
@ -325,7 +313,6 @@ export default function useContractManagement() {
|
||||
contract_type: payload.contract_type,
|
||||
lang: payload.lang,
|
||||
user_type: payload.user_type,
|
||||
tax_mode: payload.tax_mode,
|
||||
state: payload.state,
|
||||
descriptionLength: payload.description ? payload.description.length : 0,
|
||||
file: typeof File !== 'undefined' && file instanceof File ? { name: file.name, type: file.type, size: file.size } : null,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user