Vertrag: Kunden-/Vertragsnummer bei Vertriebsplattform
Viele Vertriebsplattformen vergeben eigene Nummern, die nicht mit denen des Endanbieters identisch sind. Zwei neue optionale Felder unter "Anbieter & Tarif". - Schema: Contract.customerNumberAtSalesPlatform + contractNumberAtSalesPlatform, Migration mit IF NOT EXISTS. - ContractForm: zwei neue Inputs direkt unter den entsprechenden Provider-Feldern. - ContractDetail: eigene Zeilen mit CopyButton. - Audit-Log-Mapping + Renewal-Copy + XSS-Strip-Whitelist mitgezogen. - Bonus: contractNumberAtProvider war im Renewal-Copy und Audit- Label-Mapping fehlend – mitkorrigiert.
This commit is contained in:
@@ -0,0 +1,7 @@
|
|||||||
|
-- Vertrieb-/Provider-Trennung: viele Plattformen vergeben eigene Kunden-/
|
||||||
|
-- Vertragsnummern, die nicht mit denen beim Endanbieter identisch sind.
|
||||||
|
-- Zwei neue optionale Felder unter "Anbieter & Tarif".
|
||||||
|
|
||||||
|
ALTER TABLE `Contract`
|
||||||
|
ADD COLUMN IF NOT EXISTS `customerNumberAtSalesPlatform` VARCHAR(191) NULL,
|
||||||
|
ADD COLUMN IF NOT EXISTS `contractNumberAtSalesPlatform` VARCHAR(191) NULL;
|
||||||
@@ -687,6 +687,8 @@ model Contract {
|
|||||||
tariffName String?
|
tariffName String?
|
||||||
customerNumberAtProvider String?
|
customerNumberAtProvider String?
|
||||||
contractNumberAtProvider String? // Vertragsnummer beim Anbieter
|
contractNumberAtProvider String? // Vertragsnummer beim Anbieter
|
||||||
|
customerNumberAtSalesPlatform String? // Kundennummer bei der Vertriebsplattform
|
||||||
|
contractNumberAtSalesPlatform String? // Vertragsnummer bei der Vertriebsplattform
|
||||||
priceFirst12Months String? // Preis erste 12 Monate
|
priceFirst12Months String? // Preis erste 12 Monate
|
||||||
priceFrom13Months String? // Preis ab 13. Monat
|
priceFrom13Months String? // Preis ab 13. Monat
|
||||||
priceAfter24Months String? // Preis nach 24 Monaten
|
priceAfter24Months String? // Preis nach 24 Monaten
|
||||||
|
|||||||
@@ -203,6 +203,9 @@ export async function updateContract(req: AuthRequest, res: Response): Promise<v
|
|||||||
const fieldLabels: Record<string, string> = {
|
const fieldLabels: Record<string, string> = {
|
||||||
status: 'Status', startDate: 'Vertragsbeginn', endDate: 'Vertragsende',
|
status: 'Status', startDate: 'Vertragsbeginn', endDate: 'Vertragsende',
|
||||||
portalUsername: 'Portal-Benutzername', customerNumberAtProvider: 'Kundennummer beim Anbieter',
|
portalUsername: 'Portal-Benutzername', customerNumberAtProvider: 'Kundennummer beim Anbieter',
|
||||||
|
contractNumberAtProvider: 'Vertragsnummer beim Anbieter',
|
||||||
|
customerNumberAtSalesPlatform: 'Kundennummer bei Vertriebsplattform',
|
||||||
|
contractNumberAtSalesPlatform: 'Vertragsnummer bei Vertriebsplattform',
|
||||||
providerId: 'Anbieter', tariffId: 'Tarif', cancellationPeriodId: 'Kündigungsfrist',
|
providerId: 'Anbieter', tariffId: 'Tarif', cancellationPeriodId: 'Kündigungsfrist',
|
||||||
contractDurationId: 'Vertragslaufzeit', platformId: 'Vertriebsplattform',
|
contractDurationId: 'Vertragslaufzeit', platformId: 'Vertriebsplattform',
|
||||||
cancellationDate: 'Kündigungsdatum', cancellationSentDate: 'Kündigung gesendet am',
|
cancellationDate: 'Kündigungsdatum', cancellationSentDate: 'Kündigung gesendet am',
|
||||||
|
|||||||
@@ -203,6 +203,8 @@ interface ContractCreateData {
|
|||||||
providerName?: string;
|
providerName?: string;
|
||||||
tariffName?: string;
|
tariffName?: string;
|
||||||
customerNumberAtProvider?: string;
|
customerNumberAtProvider?: string;
|
||||||
|
customerNumberAtSalesPlatform?: string;
|
||||||
|
contractNumberAtSalesPlatform?: string;
|
||||||
priceFirst12Months?: string;
|
priceFirst12Months?: string;
|
||||||
priceFrom13Months?: string;
|
priceFrom13Months?: string;
|
||||||
priceAfter24Months?: string;
|
priceAfter24Months?: string;
|
||||||
@@ -896,6 +898,9 @@ export async function createRenewalContract(previousContractId: number) {
|
|||||||
providerName: previousContract.providerName,
|
providerName: previousContract.providerName,
|
||||||
tariffName: previousContract.tariffName,
|
tariffName: previousContract.tariffName,
|
||||||
customerNumberAtProvider: previousContract.customerNumberAtProvider,
|
customerNumberAtProvider: previousContract.customerNumberAtProvider,
|
||||||
|
contractNumberAtProvider: previousContract.contractNumberAtProvider,
|
||||||
|
customerNumberAtSalesPlatform: previousContract.customerNumberAtSalesPlatform,
|
||||||
|
contractNumberAtSalesPlatform: previousContract.contractNumberAtSalesPlatform,
|
||||||
portalUsername: previousContract.portalUsername,
|
portalUsername: previousContract.portalUsername,
|
||||||
portalPasswordEncrypted: previousContract.portalPasswordEncrypted,
|
portalPasswordEncrypted: previousContract.portalPasswordEncrypted,
|
||||||
commission: previousContract.commission,
|
commission: previousContract.commission,
|
||||||
|
|||||||
@@ -75,6 +75,8 @@ const CONTRACT_DISPLAY_STRING_FIELDS = [
|
|||||||
'tariffName',
|
'tariffName',
|
||||||
'customerNumberAtProvider',
|
'customerNumberAtProvider',
|
||||||
'contractNumberAtProvider',
|
'contractNumberAtProvider',
|
||||||
|
'customerNumberAtSalesPlatform',
|
||||||
|
'contractNumberAtSalesPlatform',
|
||||||
'portalUsername',
|
'portalUsername',
|
||||||
'previousProviderName',
|
'previousProviderName',
|
||||||
'previousCustomerNumber',
|
'previousCustomerNumber',
|
||||||
|
|||||||
@@ -97,6 +97,20 @@ isolierte Instanz (keine Multi-Tenancy im Code), Provisioning + Abrechnung
|
|||||||
|
|
||||||
## ✅ Erledigt
|
## ✅ Erledigt
|
||||||
|
|
||||||
|
- [x] **🆕 Vertrag: Kunden-/Vertragsnummer bei Vertriebsplattform**
|
||||||
|
- Zwei neue optionale Felder
|
||||||
|
`Contract.customerNumberAtSalesPlatform` +
|
||||||
|
`contractNumberAtSalesPlatform`, Migration
|
||||||
|
`20260603150000_contract_sales_platform_numbers` mit
|
||||||
|
`IF NOT EXISTS`.
|
||||||
|
- Im ContractForm direkt unter „Kundennummer/Vertragsnummer beim
|
||||||
|
Anbieter" angeordnet. ContractDetail zeigt sie als eigene Zeilen
|
||||||
|
mit Copy-Button. Audit-Log-Mapping + Renewal-Copy + XSS-Strip
|
||||||
|
(CONTRACT_DISPLAY_STRING_FIELDS) mitgezogen.
|
||||||
|
- Bonus: das fehlende `contractNumberAtProvider` im Renewal-Copy
|
||||||
|
und Audit-Label-Mapping ist gleich mit drin – wurde bisher
|
||||||
|
nicht in VVL-Folgeverträge kopiert.
|
||||||
|
|
||||||
- [x] **🆕 Email-Links öffnen im neuen Tab**
|
- [x] **🆕 Email-Links öffnen im neuen Tab**
|
||||||
- In `EmailDetail` nach der DOMPurify-Sanitize jedes `<a>`-Element
|
- In `EmailDetail` nach der DOMPurify-Sanitize jedes `<a>`-Element
|
||||||
auf `target="_blank"` + `rel="noopener noreferrer"` gesetzt. Letzteres
|
auf `target="_blank"` + `rel="noopener noreferrer"` gesetzt. Letzteres
|
||||||
|
|||||||
@@ -2080,6 +2080,24 @@ export default function ContractDetail() {
|
|||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
{c.customerNumberAtSalesPlatform && (
|
||||||
|
<div>
|
||||||
|
<dt className="text-sm text-gray-500">Kundennr. Vertriebsplattform</dt>
|
||||||
|
<dd className="font-mono flex items-center gap-1">
|
||||||
|
{c.customerNumberAtSalesPlatform}
|
||||||
|
<CopyButton value={c.customerNumberAtSalesPlatform} />
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{c.contractNumberAtSalesPlatform && (
|
||||||
|
<div>
|
||||||
|
<dt className="text-sm text-gray-500">Vertragsnr. Vertriebsplattform</dt>
|
||||||
|
<dd className="font-mono flex items-center gap-1">
|
||||||
|
{c.contractNumberAtSalesPlatform}
|
||||||
|
<CopyButton value={c.contractNumberAtSalesPlatform} />
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
{c.salesPlatform && (
|
{c.salesPlatform && (
|
||||||
<div>
|
<div>
|
||||||
<dt className="text-sm text-gray-500">Vertriebsplattform</dt>
|
<dt className="text-sm text-gray-500">Vertriebsplattform</dt>
|
||||||
|
|||||||
@@ -302,6 +302,8 @@ export default function ContractForm() {
|
|||||||
tariffName: c.tariffName || '',
|
tariffName: c.tariffName || '',
|
||||||
customerNumberAtProvider: c.customerNumberAtProvider || '',
|
customerNumberAtProvider: c.customerNumberAtProvider || '',
|
||||||
contractNumberAtProvider: c.contractNumberAtProvider || '',
|
contractNumberAtProvider: c.contractNumberAtProvider || '',
|
||||||
|
customerNumberAtSalesPlatform: c.customerNumberAtSalesPlatform || '',
|
||||||
|
contractNumberAtSalesPlatform: c.contractNumberAtSalesPlatform || '',
|
||||||
priceFirst12Months: c.priceFirst12Months || '',
|
priceFirst12Months: c.priceFirst12Months || '',
|
||||||
priceFrom13Months: c.priceFrom13Months || '',
|
priceFrom13Months: c.priceFrom13Months || '',
|
||||||
priceAfter24Months: c.priceAfter24Months || '',
|
priceAfter24Months: c.priceAfter24Months || '',
|
||||||
@@ -556,6 +558,8 @@ export default function ContractForm() {
|
|||||||
tariffName: emptyToNull(data.tariffName),
|
tariffName: emptyToNull(data.tariffName),
|
||||||
customerNumberAtProvider: emptyToNull(data.customerNumberAtProvider),
|
customerNumberAtProvider: emptyToNull(data.customerNumberAtProvider),
|
||||||
contractNumberAtProvider: emptyToNull(data.contractNumberAtProvider),
|
contractNumberAtProvider: emptyToNull(data.contractNumberAtProvider),
|
||||||
|
customerNumberAtSalesPlatform: emptyToNull(data.customerNumberAtSalesPlatform),
|
||||||
|
contractNumberAtSalesPlatform: emptyToNull(data.contractNumberAtSalesPlatform),
|
||||||
priceFirst12Months: emptyToNull(data.priceFirst12Months),
|
priceFirst12Months: emptyToNull(data.priceFirst12Months),
|
||||||
priceFrom13Months: emptyToNull(data.priceFrom13Months),
|
priceFrom13Months: emptyToNull(data.priceFrom13Months),
|
||||||
priceAfter24Months: emptyToNull(data.priceAfter24Months),
|
priceAfter24Months: emptyToNull(data.priceAfter24Months),
|
||||||
@@ -952,6 +956,8 @@ export default function ContractForm() {
|
|||||||
/>
|
/>
|
||||||
<Input label="Kundennummer beim Anbieter" {...register('customerNumberAtProvider')} />
|
<Input label="Kundennummer beim Anbieter" {...register('customerNumberAtProvider')} />
|
||||||
<Input label="Vertragsnummer beim Anbieter" {...register('contractNumberAtProvider')} />
|
<Input label="Vertragsnummer beim Anbieter" {...register('contractNumberAtProvider')} />
|
||||||
|
<Input label="Kundennummer bei Vertriebsplattform" {...register('customerNumberAtSalesPlatform')} />
|
||||||
|
<Input label="Vertragsnummer bei Vertriebsplattform" {...register('contractNumberAtSalesPlatform')} />
|
||||||
<Input label="Provision (€)" type="number" step="0.01" {...register('commission')} />
|
<Input label="Provision (€)" type="number" step="0.01" {...register('commission')} />
|
||||||
<Input label="Preis erste 12 Monate" {...register('priceFirst12Months')} placeholder="z.B. 29,99 €/Monat" />
|
<Input label="Preis erste 12 Monate" {...register('priceFirst12Months')} placeholder="z.B. 29,99 €/Monat" />
|
||||||
<Input label="Preis ab 13. Monat" {...register('priceFrom13Months')} placeholder="z.B. 39,99 €/Monat" />
|
<Input label="Preis ab 13. Monat" {...register('priceFrom13Months')} placeholder="z.B. 39,99 €/Monat" />
|
||||||
|
|||||||
@@ -454,6 +454,8 @@ export interface Contract {
|
|||||||
tariffName?: string;
|
tariffName?: string;
|
||||||
customerNumberAtProvider?: string;
|
customerNumberAtProvider?: string;
|
||||||
contractNumberAtProvider?: string;
|
contractNumberAtProvider?: string;
|
||||||
|
customerNumberAtSalesPlatform?: string;
|
||||||
|
contractNumberAtSalesPlatform?: string;
|
||||||
priceFirst12Months?: string;
|
priceFirst12Months?: string;
|
||||||
priceFrom13Months?: string;
|
priceFrom13Months?: string;
|
||||||
priceAfter24Months?: string;
|
priceAfter24Months?: string;
|
||||||
|
|||||||
Reference in New Issue
Block a user