Einrichtung: Konditionen und CRM-Status beim Anlegen direkt sichtbar machen

- Backend (omsorgCore): CreateFacilityRequest um Konditionen- und CRM-Status-Felder erweitert, identisch mit UpdateFacilityRequest
- FacilitiesController.Create: Gleiche Validierung wie Update (CrmStatus-Allowlist, FollowUp-Logik, Konditions-Wertebereich), aber ohne Transition-Check (kein Vorzustand beim Anlegen)
- Gemeinsame Validierungslogik in ValidateCrmStatusAndFollowUpAsync extrahiert, um Code-Duplizierung zu vermeiden
- omsorgCore/CLAUDE.md aktualisiert: Konditionen sind jetzt auch beim Anlegen setzbar
- API-Clients neu generiert (TypeScript und PHP)
- Frontend (omsorgapp): FacilityForm.jsx: includeCrmStatus-Flag entfernt, Konditionen und CRM-Status immer sichtbar
- CreateFacilityDialog.jsx: CRM-Status-Auswahl + FollowUpDaysDialog-Workflow (analog zu Edit-Dialog)
- EditFacilityDialog.jsx: vereinfacht (includeCrmStatus-Prop entfernt)

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
Felix Kemmler
2026-08-10 22:33:27 +02:00
co-authored by Claude Haiku 4.5
parent 52fbec3214
commit e1fbfadd17
9 changed files with 842 additions and 90 deletions
@@ -63,9 +63,11 @@ export function facilityToFormValues(facility) {
};
}
export function facilityFormToPayload(form, { includeCrmStatus = false, followUpDays = null } = {}) {
const payload = {
export function facilityFormToPayload(form, { followUpDays = null } = {}) {
return {
name: form.name.trim(),
crmStatus: form.crmStatus,
followUpDays: followUpDays,
facilityType: form.facilityType || null,
website: form.website.trim() || null,
street: form.street.trim() || null,
@@ -76,38 +78,29 @@ export function facilityFormToPayload(form, { includeCrmStatus = false, followUp
billingPostalCode: form.billingPostalCode.trim() || null,
billingCity: form.billingCity.trim() || null,
billingCountry: form.billingCountry.trim() || null,
billingRate: parseGermanDecimal(form.billingRate),
nightSurchargePercent: parseGermanDecimal(form.nightSurchargePercent),
saturdaySurchargePercent: parseGermanDecimal(form.saturdaySurchargePercent),
sundaySurchargePercent: parseGermanDecimal(form.sundaySurchargePercent),
holidaySurchargePercent: parseGermanDecimal(form.holidaySurchargePercent),
travelCostMode: form.travelCostMode || "Pauschale",
travelCostRate: parseGermanDecimal(form.travelCostRate),
travelCostPerKm: parseGermanDecimal(form.travelCostPerKm),
mealAllowanceRate: parseGermanDecimal(form.mealAllowanceRate),
minimumHours: parseGermanDecimal(form.minimumHours),
billingInterval: form.billingInterval || null,
paymentTermDays: form.paymentTermDays === "" ? null : Number(form.paymentTermDays),
individualAgreements: form.individualAgreements.trim() || null,
};
if (includeCrmStatus) {
payload.crmStatus = form.crmStatus;
payload.followUpDays = followUpDays;
payload.billingRate = parseGermanDecimal(form.billingRate);
payload.nightSurchargePercent = parseGermanDecimal(form.nightSurchargePercent);
payload.saturdaySurchargePercent = parseGermanDecimal(form.saturdaySurchargePercent);
payload.sundaySurchargePercent = parseGermanDecimal(form.sundaySurchargePercent);
payload.holidaySurchargePercent = parseGermanDecimal(form.holidaySurchargePercent);
payload.travelCostMode = form.travelCostMode || "Pauschale";
payload.travelCostRate = parseGermanDecimal(form.travelCostRate);
payload.travelCostPerKm = parseGermanDecimal(form.travelCostPerKm);
payload.mealAllowanceRate = parseGermanDecimal(form.mealAllowanceRate);
payload.minimumHours = parseGermanDecimal(form.minimumHours);
payload.billingInterval = form.billingInterval || null;
payload.paymentTermDays = form.paymentTermDays === "" ? null : Number(form.paymentTermDays);
payload.individualAgreements = form.individualAgreements.trim() || null;
}
return payload;
}
export default function FacilityForm({ form, onChange, includeCrmStatus = false, currentCrmStatus = null }) {
export default function FacilityForm({ form, onChange, currentCrmStatus = null }) {
const { items: crmStatusOptions } = useValueListItems("CrmStatus");
const { items: facilityTypes } = useValueListItems("FacilityType");
const { items: billingIntervals } = useValueListItems("BillingInterval");
const [transitions, setTransitions] = useState([]);
useEffect(() => {
if (!includeCrmStatus) return;
let cancelled = false;
window.omsorg.valueLists.listTransitions("CrmStatus").then((result) => {
if (!cancelled) {
@@ -118,7 +111,7 @@ export default function FacilityForm({ form, onChange, includeCrmStatus = false,
return () => {
cancelled = true;
};
}, [includeCrmStatus]);
}, []);
const currentCrmStatusItem = crmStatusOptions.find((option) => option.value === currentCrmStatus);
const selectableCrmStatusOptions = currentCrmStatusItem
@@ -144,18 +137,16 @@ export default function FacilityForm({ form, onChange, includeCrmStatus = false,
<input value={form.name} onChange={updateField("name")} required />
</label>
{includeCrmStatus && (
<label className="form-field">
<span>CRM-Status</span>
<select value={form.crmStatus} onChange={updateField("crmStatus")}>
{selectableCrmStatusOptions.map((option) => (
<option key={option.id} value={option.value}>
{option.value}
</option>
))}
</select>
</label>
)}
<label className="form-field">
<span>CRM-Status</span>
<select value={form.crmStatus} onChange={updateField("crmStatus")}>
{selectableCrmStatusOptions.map((option) => (
<option key={option.id} value={option.value}>
{option.value}
</option>
))}
</select>
</label>
<label className="form-field">
<span>Art der Einrichtung</span>
@@ -226,9 +217,8 @@ export default function FacilityForm({ form, onChange, includeCrmStatus = false,
</label>
</fieldset>
{includeCrmStatus && (
<fieldset className="form-field form-field--fieldset">
<legend>Konditionen</legend>
<fieldset className="form-field form-field--fieldset">
<legend>Konditionen</legend>
<label className="form-field">
<span>Verrechnungssatz (EUR/Std.)</span>
@@ -319,7 +309,6 @@ export default function FacilityForm({ form, onChange, includeCrmStatus = false,
<input value={form.individualAgreements} onChange={updateField("individualAgreements")} />
</label>
</fieldset>
)}
</div>
);
}