Gutschriften Phase 2a: Kleinunternehmer-Flag am Kunden + USt-Default
Kunden-Feld vatExempt (Kleinunternehmer/USt-befreit §19) + Migration (ADD COLUMN IF NOT EXISTS). Checkbox im Kundenformular nur fuer Firmenkunden. Gutschrift-Vorbelegung vatRelevant wird aus dem Kunden abgeleitet: Firmenkunde ohne USt-Befreiung -> USt-relevant an (Netto), sonst aus (wie Privat). Jede Gutschrift speichert ihren eigenen Snapshot, ein spaeterer Statuswechsel des Kunden aendert bestehende Gutschriften nicht. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
-- Kleinunternehmer/USt-befreit-Flag am Kunden. Steuert nur die Vorbelegung
|
||||
-- neuer Gutschriften (Firmenkunde mit vatExempt=true -> keine USt-Vorbelegung).
|
||||
ALTER TABLE `Customer` ADD COLUMN IF NOT EXISTS `vatExempt` BOOLEAN NOT NULL DEFAULT false;
|
||||
@@ -156,6 +156,12 @@ model Customer {
|
||||
phone String?
|
||||
mobile String?
|
||||
taxNumber String?
|
||||
// Kleinunternehmer nach §19 UStG bzw. sonst USt-befreit: dann wird der
|
||||
// (Firmen-)Kunde bei Gutschriften wie ein Privatkunde behandelt (keine USt),
|
||||
// bis er ggf. USt-pflichtig wird. Steuert nur die VORBELEGUNG neuer
|
||||
// Gutschriften; bereits erstellte Gutschriften speichern ihren eigenen
|
||||
// Snapshot und bleiben von einem späteren Wechsel unberührt.
|
||||
vatExempt Boolean @default(false)
|
||||
businessRegistrationPath String? // PDF-Pfad zur Gewerbeanmeldung
|
||||
commercialRegisterPath String? // PDF-Pfad zum Handelsregisterauszug
|
||||
commercialRegisterNumber String? // Handelsregisternummer (Text)
|
||||
|
||||
@@ -60,11 +60,11 @@ export async function formDefaults(req: AuthRequest, res: Response): Promise<voi
|
||||
if (blockPortal(req, res)) return;
|
||||
const contractId = idParam(req, res, 'contractId');
|
||||
if (contractId === null) return;
|
||||
const [customerType, nextNumber] = await Promise.all([
|
||||
creditNoteService.getDefaultCustomerType(contractId),
|
||||
const [defaults, nextNumber] = await Promise.all([
|
||||
creditNoteService.getCreditNoteDefaults(contractId),
|
||||
numberRangeService.previewNextNumber(),
|
||||
]);
|
||||
res.json({ success: true, data: { customerType, nextNumber } } as ApiResponse);
|
||||
res.json({ success: true, data: { ...defaults, nextNumber } } as ApiResponse);
|
||||
} catch (error) {
|
||||
handleError(res, error, 'Fehler beim Laden der Vorbelegung');
|
||||
}
|
||||
|
||||
@@ -132,13 +132,26 @@ export async function getCreditNoteById(id: number) {
|
||||
return prisma.creditNote.findUnique({ where: { id } });
|
||||
}
|
||||
|
||||
// Ermittelt den Default-Kundentyp aus dem Vertrag (Firma vs. Privat).
|
||||
export async function getDefaultCustomerType(contractId: number): Promise<CreditNoteCustomerType> {
|
||||
// Ermittelt die Formular-Vorbelegung aus dem Kunden:
|
||||
// - customerType: Firma vs. Privat (aus Kunde.type)
|
||||
// - vatRelevant : Default nur bei Firmenkunde OHNE USt-Befreiung
|
||||
// (Kleinunternehmer §19 → wie Privat, keine USt-Vorbelegung).
|
||||
// WICHTIG: Das ist nur die Vorbelegung. Jede angelegte Gutschrift speichert
|
||||
// ihren eigenen Snapshot; ein späterer Statuswechsel des Kunden ändert
|
||||
// bestehende Gutschriften nicht.
|
||||
export async function getCreditNoteDefaults(
|
||||
contractId: number,
|
||||
): Promise<{ customerType: CreditNoteCustomerType; vatRelevant: boolean }> {
|
||||
const contract = await prisma.contract.findUnique({
|
||||
where: { id: contractId },
|
||||
select: { customer: { select: { type: true } } },
|
||||
select: { customer: { select: { type: true, vatExempt: true } } },
|
||||
});
|
||||
return contract?.customer?.type === 'BUSINESS' ? 'FIRMA' : 'PRIVAT';
|
||||
const isBusiness = contract?.customer?.type === 'BUSINESS';
|
||||
const vatExempt = !!contract?.customer?.vatExempt;
|
||||
return {
|
||||
customerType: isBusiness ? 'FIRMA' : 'PRIVAT',
|
||||
vatRelevant: isBusiness && !vatExempt,
|
||||
};
|
||||
}
|
||||
|
||||
export async function createCreditNote(
|
||||
|
||||
@@ -146,6 +146,7 @@ export async function createCustomer(data: {
|
||||
phone?: string;
|
||||
mobile?: string;
|
||||
taxNumber?: string;
|
||||
vatExempt?: boolean;
|
||||
businessRegistration?: string;
|
||||
commercialRegister?: string;
|
||||
notes?: string;
|
||||
@@ -173,6 +174,7 @@ export async function updateCustomer(
|
||||
phone?: string;
|
||||
mobile?: string;
|
||||
taxNumber?: string;
|
||||
vatExempt?: boolean;
|
||||
businessRegistration?: string;
|
||||
commercialRegister?: string;
|
||||
notes?: string;
|
||||
|
||||
@@ -618,6 +618,7 @@ const CUSTOMER_UPDATABLE_FIELDS = [
|
||||
'phone',
|
||||
'mobile',
|
||||
'taxNumber',
|
||||
'vatExempt',
|
||||
'commercialRegisterNumber',
|
||||
'notes',
|
||||
'portalEnabled',
|
||||
|
||||
Reference in New Issue
Block a user