MaLo-ID an die Lieferadresse (Strom/Gas) + Address-owner-Regression gefixt
MaLo-ID (Marktlokation) gehoert zur (Liefer-)Adresse, nicht zum Vertrag.
Address bekommt maloIdElectricity + maloIdGas (getrennte Marktlokationen je
Sparte), pflegbar im AddressModal (nur Lieferadresse). Im Vertrag ist die
MaLo-ID jetzt ein Lesefeld, das je nach Vertragstyp die MaLo der gewaehlten
Lieferadresse zeigt; ContractDetail/-Modal ebenso.
Schema + Migration 20260814100000: 2 Spalten (idempotent) + Daten-Migration
(bestehende EnergyContractDetails.maloId -> jeweilige Lieferadresse,
ELECTRICITY->maloIdElectricity / GAS->maloIdGas). Migrationslogik verifiziert.
Dabei einen selbst verursachten Regressions-Bug gefixt: beim R156-Umbau waren
die 10 owner*-Adressfelder aus der Address-Whitelist gefallen -> Eigentuemer-
Sektion speicherte seit cb21a2c nicht mehr. Address-Whitelist jetzt via
Pick-Helper, programmatisch gegen alle DB-Spalten abgeglichen (owner* + MaLo
drin, id/customerId/Timestamps raus). BankCard/Document gegengeprueft: ok
(nur documentPath bewusst upload-only ausgeschlossen).
Verifiziert: tsc+build gruen; owner + maloId speichern wieder, Injection
(id/customerId) blockiert; Daten-Migration Strom->Strom / Gas->Gas.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
-- MaLo-IDs (Marktlokation) je Sparte an die Adresse: die MaLo gehört zur
|
||||
-- (Liefer-)Adresse, nicht zum Vertrag. Strom + Gas getrennt.
|
||||
ALTER TABLE `Address` ADD COLUMN IF NOT EXISTS `maloIdElectricity` VARCHAR(191) NULL;
|
||||
ALTER TABLE `Address` ADD COLUMN IF NOT EXISTS `maloIdGas` VARCHAR(191) NULL;
|
||||
|
||||
-- Daten-Migration: bestehende Vertrags-MaLo-IDs an die jeweilige Lieferadresse
|
||||
-- übertragen – je nach Vertragstyp in Strom- bzw. Gas-Feld. Nur setzen, wenn
|
||||
-- am Ziel-Adressfeld noch nichts steht (idempotent, kein Überschreiben).
|
||||
UPDATE `Address` a
|
||||
JOIN `Contract` c ON c.addressId = a.id
|
||||
JOIN `EnergyContractDetails` ecd ON ecd.contractId = c.id
|
||||
SET a.maloIdElectricity = ecd.maloId
|
||||
WHERE c.type = 'ELECTRICITY'
|
||||
AND ecd.maloId IS NOT NULL AND ecd.maloId <> ''
|
||||
AND (a.maloIdElectricity IS NULL OR a.maloIdElectricity = '');
|
||||
|
||||
UPDATE `Address` a
|
||||
JOIN `Contract` c ON c.addressId = a.id
|
||||
JOIN `EnergyContractDetails` ecd ON ecd.contractId = c.id
|
||||
SET a.maloIdGas = ecd.maloId
|
||||
WHERE c.type = 'GAS'
|
||||
AND ecd.maloId IS NOT NULL AND ecd.maloId <> ''
|
||||
AND (a.maloIdGas IS NULL OR a.maloIdGas = '');
|
||||
@@ -327,6 +327,10 @@ model Address {
|
||||
city String
|
||||
country String @default("Deutschland")
|
||||
isDefault Boolean @default(false)
|
||||
// Marktlokations-IDs (MaLo) je Sparte – die MaLo gehört zur (Liefer-)Adresse,
|
||||
// nicht zum Vertrag. Strom- und Gas-Marktlokation sind getrennt.
|
||||
maloIdElectricity String? // MaLo-ID Strom
|
||||
maloIdGas String? // MaLo-ID Gas
|
||||
// Eigentümer (leer = Kunde ist selbst Eigentümer)
|
||||
ownerCompany String?
|
||||
ownerFirstName String?
|
||||
|
||||
@@ -218,6 +218,27 @@ export async function getCustomerAddresses(customerId: number) {
|
||||
});
|
||||
}
|
||||
|
||||
// Feld-Whitelist für Address-Create/Update (Mass-Assignment-Schutz, konsistent
|
||||
// zu R156; autoritativ aus den DB-Spalten abgeleitet). BEWUSST NICHT enthalten:
|
||||
// id/customerId/createdAt/updatedAt. Enthält die Eigentümer-Felder (owner*) –
|
||||
// die waren beim ersten R156-Umbau versehentlich rausgefallen (Regression) –
|
||||
// sowie die neuen MaLo-IDs.
|
||||
const ADDRESS_SCALAR_FIELDS = [
|
||||
'type', 'street', 'houseNumber', 'postalCode', 'city', 'country', 'isDefault',
|
||||
'maloIdElectricity', 'maloIdGas',
|
||||
'ownerCompany', 'ownerFirstName', 'ownerLastName', 'ownerStreet', 'ownerHouseNumber',
|
||||
'ownerPostalCode', 'ownerCity', 'ownerPhone', 'ownerMobile', 'ownerEmail',
|
||||
] as const;
|
||||
|
||||
function pickAddressScalars(data: unknown): Record<string, unknown> {
|
||||
const src = (data && typeof data === 'object') ? (data as Record<string, unknown>) : {};
|
||||
const out: Record<string, unknown> = {};
|
||||
for (const key of ADDRESS_SCALAR_FIELDS) {
|
||||
if (key in src) out[key] = src[key];
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
export async function createAddress(
|
||||
customerId: number,
|
||||
data: {
|
||||
@@ -238,17 +259,12 @@ export async function createAddress(
|
||||
});
|
||||
}
|
||||
|
||||
// Explizite Feld-Whitelist (Mass-Assignment-Schutz, Pentest R155).
|
||||
// Feld-Whitelist (Mass-Assignment-Schutz) inkl. owner*/MaLo – siehe
|
||||
// ADDRESS_SCALAR_FIELDS. customerId kommt ausschließlich aus dem Parameter.
|
||||
return prisma.address.create({
|
||||
data: {
|
||||
customerId,
|
||||
type: data.type,
|
||||
street: data.street,
|
||||
houseNumber: data.houseNumber,
|
||||
postalCode: data.postalCode,
|
||||
city: data.city,
|
||||
country: data.country,
|
||||
isDefault: data.isDefault,
|
||||
...(pickAddressScalars(data) as any),
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -279,18 +295,11 @@ export async function updateAddress(
|
||||
});
|
||||
}
|
||||
|
||||
// Explizite Feld-Whitelist (Mass-Assignment-Schutz, Pentest R155).
|
||||
// Feld-Whitelist (Mass-Assignment-Schutz) inkl. owner*/MaLo – siehe
|
||||
// ADDRESS_SCALAR_FIELDS.
|
||||
return prisma.address.update({
|
||||
where: { id },
|
||||
data: {
|
||||
type: data.type,
|
||||
street: data.street,
|
||||
houseNumber: data.houseNumber,
|
||||
postalCode: data.postalCode,
|
||||
city: data.city,
|
||||
country: data.country,
|
||||
isDefault: data.isDefault,
|
||||
},
|
||||
data: pickAddressScalars(data) as any,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user