Skip to content
Snippets Groups Projects
Verified Commit aa0f2f5f authored by Sebastian Bergandy's avatar Sebastian Bergandy :keyboard:
Browse files

OZG-6185 remove bescheid from vorgang detail

parent d9a388fe
Branches
Tags
1 merge request!90Ozg 6185 remove old bescheid code
Showing
with 4 additions and 3389 deletions
......@@ -23,5 +23,4 @@
*/
export * from './lib/aktenzeichen-edit-dialog/aktenzeichen-edit-dialog.component';
export * from './lib/aktenzeichen-editable/aktenzeichen-editable.component';
export * from './lib/vorgang-detail-page/vorgang-detail-bescheiden/bescheiden.model';
export * from './lib/vorgang-detail.module';
......@@ -21,22 +21,16 @@
* Die sprachspezifischen Genehmigungen und Beschränkungen
* unter der Lizenz sind dem Lizenztext zu entnehmen.
*/
import { BescheidWizardContainerComponent } from '@alfa-client/bescheid';
import { BescheidWizardContainerComponent, BescheidWizardDialogData } from '@alfa-client/bescheid';
import { BescheidWizardDialogResult } from '@alfa-client/bescheid-shared';
import { CommandResource } from '@alfa-client/command-shared';
import { createEmptyStateResource, isNotNil, StateResource } from '@alfa-client/tech-shared';
import { OzgcloudDialogService } from '@alfa-client/ui';
import {
VorgangCommandService,
VorgangService,
VorgangWithEingangLinkRel,
VorgangWithEingangResource,
} from '@alfa-client/vorgang-shared';
import { VorgangCommandService, VorgangService, VorgangWithEingangLinkRel, VorgangWithEingangResource, } from '@alfa-client/vorgang-shared';
import { DialogRef } from '@angular/cdk/dialog';
import { Component, Input, OnInit } from '@angular/core';
import { hasLink } from '@ngxp/rest';
import { Observable, of } from 'rxjs';
import { BescheidenDialogData } from '../../vorgang-detail-page/vorgang-detail-bescheiden/bescheiden.model';
@Component({
selector: 'alfa-bescheiden-button',
......@@ -87,10 +81,10 @@ export class BescheidenButtonComponent implements OnInit {
}
public openBescheidenWizard(): void {
const dialogData: BescheidenDialogData = { vorgangWithEingangResource: this.vorgang };
const dialogData: BescheidWizardDialogData = { vorgangWithEingangResource: this.vorgang };
const dialogRef: DialogRef<BescheidWizardDialogResult> = this.ozgcloudDialogService.openWizard<
BescheidWizardContainerComponent,
BescheidenDialogData,
BescheidWizardDialogData,
BescheidWizardDialogResult
>(BescheidWizardContainerComponent, dialogData);
dialogRef.closed.subscribe((dialogResult: BescheidWizardDialogResult) => this.handleBescheidWizardClosed(dialogResult));
......
/*
* Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den
* Ministerpräsidenten des Landes Schleswig-Holstein
* Staatskanzlei
* Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
*
* Lizenziert unter der EUPL, Version 1.2 oder - sobald
* diese von der Europäischen Kommission genehmigt wurden -
* Folgeversionen der EUPL ("Lizenz");
* Sie dürfen dieses Werk ausschließlich gemäß
* dieser Lizenz nutzen.
* Eine Kopie der Lizenz finden Sie hier:
*
* https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
*
* Sofern nicht durch anwendbare Rechtsvorschriften
* gefordert oder in schriftlicher Form vereinbart, wird
* die unter der Lizenz verbreitete Software "so wie sie
* ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
* ausdrücklich oder stillschweigend - verbreitet.
* Die sprachspezifischen Genehmigungen und Beschränkungen
* unter der Lizenz sind dem Lizenztext zu entnehmen.
*/
import {
Bescheid,
BescheidLinkRel,
BescheidResource,
BescheidSendBy,
BescheidService,
DocumentResource,
} from '@alfa-client/bescheid-shared';
import { BinaryFileResource } from '@alfa-client/binary-file-shared';
import { CommandLinkRel, CommandResource } from '@alfa-client/command-shared';
import {
EMPTY_STRING,
StateResource,
createStateResource,
formatForDatabase,
} from '@alfa-client/tech-shared';
import { Mock, mock, useFromMock } from '@alfa-client/test-utils';
import { VorgangWithEingangResource } from '@alfa-client/vorgang-shared';
import { registerLocaleData } from '@angular/common';
import localeDe from '@angular/common/locales/de';
import { fakeAsync, tick } from '@angular/core/testing';
import { UntypedFormBuilder } from '@angular/forms';
import { faker } from '@faker-js/faker';
import { Resource, ResourceUri } from '@ngxp/rest';
import { cold } from 'jest-marbles';
import {
createCommandResource,
createCommandStateResource,
} from 'libs/command-shared/test/command';
import { toResource } from 'libs/tech-shared/test/resource';
import { of } from 'rxjs';
import {
createBescheid,
createBescheidResource,
} from '../../../../../bescheid-shared/src/test/bescheid';
import { createDocumentResource } from '../../../../../bescheid-shared/src/test/document';
import { createBinaryFileResource } from '../../../../../binary-file-shared/test/binary-file';
import { singleCold } from '../../../../../tech-shared/test/marbles';
import { createVorgangWithEingangResource } from '../../../../../vorgang-shared/test/vorgang';
import { BescheidenFormService } from './bescheiden.formservice';
registerLocaleData(localeDe);
describe('BescheidenFormService', () => {
let service: BescheidenFormService;
let bescheidService: Mock<BescheidService>;
const now: Date = new Date();
Date.now = jest.fn().mockReturnValue(now);
beforeEach(() => {
bescheidService = mock(BescheidService);
service = new BescheidenFormService(new UntypedFormBuilder(), useFromMock(bescheidService));
});
describe('constructor', () => {
it('should initialize form beschieden am', () => {
expect(service.getValue().beschiedenAm).toEqual(formatForDatabase(now));
});
it('should initialize form bewilligt', () => {
expect(service.getValue().bewilligt).toBeTruthy();
});
it('should initialize form send by', () => {
expect(service.getValue().sendBy).toEqual(BescheidSendBy.NACHRICHT);
});
it('should initialize form bescheid dokument', () => {
expect(service.getValue().bescheidDocument).toBeNull();
});
it('should initialize form attachments', () => {
expect(service.getValue().attachments).toEqual([]);
});
it('should initialize form nachricht subject', () => {
expect(service.getValue().nachrichtSubject).toEqual(EMPTY_STRING);
});
it('should initialize form nachricht text', () => {
expect(service.getValue().nachrichtText).toEqual(EMPTY_STRING);
});
});
describe('init', () => {
it('should subscribe to sendBy', () => {
service.subscribeToSendBy = jest.fn();
service.init();
expect(service.subscribeToSendBy).toHaveBeenCalled();
});
});
describe('subscribe to send by', () => {
it('should update send by on value change', () => {
service.updateSendByManual = jest.fn();
service.subscribeToSendBy();
service.form.controls[BescheidenFormService.FIELD_SEND_BY].patchValue(BescheidSendBy.MANUAL);
expect(service.updateSendByManual).toHaveBeenCalled();
});
});
describe('update send by manual', () => {
it('should set to true if its manual', () => {
service.updateSendByManual(BescheidSendBy.MANUAL);
expect(service.sendByManual.value).toBeTruthy();
});
it('should set to false if its nachricht', () => {
service.updateSendByManual(BescheidSendBy.NACHRICHT);
expect(service.sendByManual.value).toBeFalsy();
});
it('should not emit if value not changed', () => {
service.sendByManual.next(true);
service.sendByManual.next = jest.fn();
service.updateSendByManual(BescheidSendBy.MANUAL);
expect(service.sendByManual.next).not.toHaveBeenCalled();
});
it('should emit on value change', () => {
service.sendByManual.next(false);
service.sendByManual.next = jest.fn();
service.updateSendByManual(BescheidSendBy.MANUAL);
expect(service.sendByManual.next).toHaveBeenCalled();
});
});
describe('initializeFormChanges', () => {
it('should emit initial form value', () => {
expect(service.getBescheidChanges()).toBeObservable(
cold('a', {
a: {
beschiedenAm: now,
bewilligt: true,
attachments: [],
bescheidDocument: null,
sendBy: BescheidSendBy.NACHRICHT,
nachrichtSubject: '',
nachrichtText: '',
},
}),
);
});
});
describe('getValue', () => {
let getFormValue: jest.Mock;
beforeEach(() => {
getFormValue = service.getFormValue = jest.fn();
getFormValue.mockReturnValue({ beschiedenAm: now, bewilligt: 'true' });
});
it('should call getFormValue', () => {
service.getValue();
expect(getFormValue).toHaveBeenCalled();
});
it('should return bescheid', () => {
const value: Bescheid = service.getValue();
expect(value).toEqual({ bewilligt: true, beschiedenAm: formatForDatabase(now) } as Bescheid);
});
});
describe('getValueWithApproved', () => {
it('should convert string bewilligt from bescheid if it is not nil', () => {
const bescheid: Bescheid = { ...createBescheid(), bewilligt: 'true' };
const result = service.getValueWithApproved(bescheid);
expect(result.bewilligt).toBeTruthy();
});
it('should set bewilligt to false if it is nil', () => {
const bescheid: Bescheid = { ...createBescheid(), bewilligt: undefined };
const result = service.getValueWithApproved(bescheid);
expect(result.bewilligt).toBeFalsy();
});
});
describe('getBescheidChanges', () => {
it('should emit initial form value', () => {
expect(service.getBescheidChanges()).toBeObservable(
singleCold({
beschiedenAm: now,
bewilligt: true,
attachments: [],
bescheidDocument: null,
sendBy: BescheidSendBy.NACHRICHT,
nachrichtSubject: '',
nachrichtText: '',
}),
);
});
});
describe('patchValues', () => {
let bescheidResource: BescheidResource;
let patch: jest.Mock;
beforeEach(() => {
bescheidResource = createBescheidResource();
patch = service.patch = jest.fn();
});
it('should call patch', () => {
service.patchValues(bescheidResource);
expect(patch).toHaveBeenCalledWith({
[BescheidenFormService.FIELD_BESCHIEDEN_AM]: bescheidResource.beschiedenAm,
[BescheidenFormService.FIELD_BEWILLIGT]: String(bescheidResource.bewilligt),
[BescheidenFormService.FIELD_BESCHEID_DOCUMENT]: null,
[BescheidenFormService.FIELD_SEND_BY]: String(bescheidResource.sendBy),
[BescheidenFormService.FIELD_NACHRICHT_SUBJECT]: bescheidResource.nachrichtSubject,
[BescheidenFormService.FIELD_NACHRICHT_TEXT]: bescheidResource.nachrichtText,
});
});
it('should call patch with undefined sendBy', () => {
const bescheidDocumentUri: ResourceUri = faker.internet.url();
service.patchValues({
...bescheidResource,
sendBy: undefined,
_links: {
...bescheidResource._links,
[BescheidLinkRel.BESCHEID_DOCUMENT]: { href: bescheidDocumentUri },
},
});
expect(patch).toHaveBeenCalledWith({
[BescheidenFormService.FIELD_BESCHIEDEN_AM]: bescheidResource.beschiedenAm,
[BescheidenFormService.FIELD_BEWILLIGT]: String(bescheidResource.bewilligt),
[BescheidenFormService.FIELD_SEND_BY]: BescheidSendBy.NACHRICHT,
[BescheidenFormService.FIELD_BESCHEID_DOCUMENT]: bescheidDocumentUri,
[BescheidenFormService.FIELD_NACHRICHT_SUBJECT]: bescheidResource.nachrichtSubject,
[BescheidenFormService.FIELD_NACHRICHT_TEXT]: bescheidResource.nachrichtText,
});
});
it('should not call patch', () => {
service.patchValues(null);
expect(patch).not.toHaveBeenCalled();
});
it('should patch attachments', () => {
service.patchValues(bescheidResource);
expect(service.getFormValue().attachments).toEqual(bescheidResource.attachments);
});
});
describe('submit', () => {
const commandStateResource: StateResource<CommandResource> =
createStateResource(createCommandResource());
beforeEach(() => {
bescheidService.updateBescheid.mockReturnValue(of(commandStateResource));
bescheidService.createBescheid.mockReturnValue(of(commandStateResource));
service.updateNachrichtOnSuccess = jest.fn().mockReturnValue(() => of(commandStateResource));
});
it('should call bescheid service update bescheid draft if is patch', (done) => {
const dummyPatchResource: Resource = toResource({});
service.patch(dummyPatchResource);
service.submit().subscribe(() => {
expect(bescheidService.updateBescheid).toHaveBeenCalledWith(service.getValue());
done();
});
});
it('should call bescheid service update bescheid draft if bescheid draft exists', (done) => {
bescheidService.existsBescheidDraft.mockReturnValue(true);
service.submit().subscribe(() => {
expect(bescheidService.updateBescheid).toHaveBeenCalledWith(service.getValue());
done();
});
});
it('should call bescheid service create bescheid draft if bescheid draft not exists', (done) => {
bescheidService.existsBescheidDraft.mockReturnValue(false);
service.submit().subscribe(() => {
expect(bescheidService.createBescheid).toHaveBeenCalledWith(
service.vorgangWithEingangResource,
service.getValue(),
);
done();
});
});
it('should update nachricht after successful bescheid creation', (done) => {
bescheidService.existsBescheidDraft.mockReturnValue(false);
service.submit().subscribe(() => {
expect(service.updateNachrichtOnSuccess).toHaveBeenCalled();
done();
});
});
});
describe('submit draft', () => {
const commandStateResource: StateResource<CommandResource> = createStateResource(
createCommandResource([CommandLinkRel.EFFECTED_RESOURCE]),
);
beforeEach(() => {
service.submit = jest.fn().mockReturnValue(of(commandStateResource));
});
it('should call submit', (done) => {
service.submitDraft().subscribe(() => {
expect(service.submit).toHaveBeenCalledWith();
done();
});
});
it('should call service to reload vorgang', fakeAsync(() => {
service.submitDraft().subscribe();
tick();
expect(bescheidService.reloadCurrentVorgang).toHaveBeenCalled();
}));
});
describe('setVorgangWithEingangResource', () => {
it('should set vorgangWithEingangResource', () => {
const vorgangWithEingangResource: VorgangWithEingangResource =
createVorgangWithEingangResource();
service.setVorgangWithEingangResource(vorgangWithEingangResource);
expect(service.vorgangWithEingangResource).toBe(vorgangWithEingangResource);
});
});
describe('getVorgangWithEingangResource', () => {
it('should reutrn vorgangWithEingangResource', () => {
const vorgangWithEingangResource: VorgangWithEingangResource =
createVorgangWithEingangResource();
service.vorgangWithEingangResource = vorgangWithEingangResource;
const vorang: VorgangWithEingangResource = service.getVorgangWithEingangResource();
expect(vorang).toBe(vorgangWithEingangResource);
});
});
describe('deleteFile', () => {
it('should emit binary file resource', (done) => {
const file: BinaryFileResource = createBinaryFileResource();
service.getFileDelete().subscribe((deletedFile: BinaryFileResource) => {
expect(deletedFile).toEqual(file);
done();
});
service.deleteFile(file);
});
});
describe('setActiveStep', () => {
it('should emit step', () => {
service.setActiveStep(3);
expect(service.getActiveStep()).toBeObservable(singleCold(3));
});
});
describe('patchNachricht', () => {
it('should patch nachrichtSubject with value from document', () => {
const documentResource: DocumentResource = createDocumentResource();
service.patchNachricht(documentResource);
expect(service.getValue().nachrichtSubject).toEqual(documentResource.nachrichtSubject);
});
it('should not patch nachrichtSubject if given', () => {
const documentResource: DocumentResource = createDocumentResource();
const bescheidResource: BescheidResource = createBescheidResource();
service.patchValues(bescheidResource);
service.patchNachricht(documentResource);
expect(service.getValue().nachrichtSubject).toEqual(bescheidResource.nachrichtSubject);
});
it('should patch nachrichtText with value from document', () => {
const documentResource: DocumentResource = createDocumentResource();
service.patchNachricht(documentResource);
expect(service.getValue().nachrichtText).toEqual(documentResource.nachrichtText);
});
it('should not patch nachrichtText if given', () => {
const documentResource: DocumentResource = createDocumentResource();
const bescheidResource: BescheidResource = createBescheidResource();
service.patchValues(bescheidResource);
service.patchNachricht(documentResource);
expect(service.getValue().nachrichtText).toEqual(bescheidResource.nachrichtText);
});
});
describe('clearNachricht', () => {
it('should patch values', () => {
const documentResource: DocumentResource = createDocumentResource();
service.patchNachricht(documentResource);
service.clearNachricht();
expect(service.getValue().nachrichtSubject).toEqual(EMPTY_STRING);
expect(service.getValue().nachrichtText).toEqual(EMPTY_STRING);
});
});
describe('setSendBy', () => {
it.each(Object.keys(BescheidSendBy))(
'should set control value to %s',
(sendBy: BescheidSendBy) => {
service.setSendBy(sendBy);
expect(service.getValue().sendBy).toEqual(sendBy);
},
);
});
describe('validateBescheidDocumentExists', () => {
it('should return true', () => {
service.getValue = jest.fn().mockReturnValue(createBescheid());
service.validateBescheidDocumentExists();
expect(service.validateBescheidDocumentExists()).toBeTruthy();
});
it('should return false', () => {
service.getValue = jest.fn().mockReturnValue({
...createBescheid(),
bescheidDocument: null,
} as Bescheid);
service.validateBescheidDocumentExists();
expect(service.validateBescheidDocumentExists()).toBeFalsy();
});
describe('getShowMissingBescheidDocumentError', () => {
it('should emit true', () => {
service.getValue = jest.fn().mockReturnValue({
...createBescheid(),
bescheidDocument: null,
} as Bescheid);
service.validateBescheidDocumentExists();
expect(service.getShowMissingBescheidDocumentError()).toBeObservable(singleCold(true));
});
it('should emit false', () => {
service.getValue = jest.fn().mockReturnValue(createBescheid());
service.validateBescheidDocumentExists();
expect(service.getShowMissingBescheidDocumentError()).toBeObservable(singleCold(false));
});
});
});
describe('isBetreffInvalid', () => {
it('should return true', () => {
service.form.controls[BescheidenFormService.FIELD_NACHRICHT_SUBJECT].setErrors({});
expect(service.isBetreffInvalid()).toBeTruthy();
});
it('should return false', () => {
service.form.controls[BescheidenFormService.FIELD_NACHRICHT_SUBJECT].setErrors(null);
expect(service.isBetreffInvalid()).toBeFalsy();
});
});
describe('isNachrichtInvalid', () => {
it('should return true', () => {
service.form.controls[BescheidenFormService.FIELD_NACHRICHT_TEXT].setErrors({});
expect(service.isNachrichtInvalid()).toBeTruthy();
});
it('should return false', () => {
service.form.controls[BescheidenFormService.FIELD_NACHRICHT_TEXT].setErrors(null);
expect(service.isNachrichtInvalid()).toBeFalsy();
});
});
describe('updateNachrichtOnSuccess', () => {
const subject: string = faker.lorem.words(3);
const text: string = faker.lorem.text();
const bescheidResource: BescheidResource = createBescheidResource();
beforeEach(() => {
bescheidResource.nachrichtSubject = subject;
bescheidResource.nachrichtText = text;
bescheidService.getBescheidDraft.mockReturnValue(of(createStateResource(bescheidResource)));
});
it('should get bescheid draft', () => {
service
.updateNachrichtOnSuccess()(
of(createCommandStateResource([CommandLinkRel.EFFECTED_RESOURCE])),
)
.subscribe();
expect(bescheidService.getBescheidDraft).toHaveBeenCalled();
});
it('should set subject', () => {
service
.updateNachrichtOnSuccess()(
of(createCommandStateResource([CommandLinkRel.EFFECTED_RESOURCE])),
)
.subscribe();
expect(service.getValue().nachrichtSubject).toEqual(subject);
});
it('should set text', () => {
service
.updateNachrichtOnSuccess()(
of(createCommandStateResource([CommandLinkRel.EFFECTED_RESOURCE])),
)
.subscribe();
expect(service.getValue().nachrichtText).toEqual(text);
});
});
});
/*
* Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den
* Ministerpräsidenten des Landes Schleswig-Holstein
* Staatskanzlei
* Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
*
* Lizenziert unter der EUPL, Version 1.2 oder - sobald
* diese von der Europäischen Kommission genehmigt wurden -
* Folgeversionen der EUPL ("Lizenz");
* Sie dürfen dieses Werk ausschließlich gemäß
* dieser Lizenz nutzen.
* Eine Kopie der Lizenz finden Sie hier:
*
* https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
*
* Sofern nicht durch anwendbare Rechtsvorschriften
* gefordert oder in schriftlicher Form vereinbart, wird
* die unter der Lizenz verbreitete Software "so wie sie
* ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
* ausdrücklich oder stillschweigend - verbreitet.
* Die sprachspezifischen Genehmigungen und Beschränkungen
* unter der Lizenz sind dem Lizenztext zu entnehmen.
*/
import {
Bescheid,
BescheidLinkRel,
BescheidResource,
BescheidSendBy,
BescheidService,
DocumentResource,
} from '@alfa-client/bescheid-shared';
import { BinaryFileResource } from '@alfa-client/binary-file-shared';
import { CommandResource, tapOnCommandSuccessfullyDone } from '@alfa-client/command-shared';
import {
AbstractFormService,
EMPTY_STRING,
StateResource,
convertToBoolean,
formatForDatabase,
isLoaded,
isNotEmpty,
isNotNil,
} from '@alfa-client/tech-shared';
import { VorgangWithEingangResource } from '@alfa-client/vorgang-shared';
import { Injectable, OnDestroy } from '@angular/core';
import { FormControl, UntypedFormArray, UntypedFormBuilder, UntypedFormControl, UntypedFormGroup } from '@angular/forms';
import { Resource, ResourceUri, getUrl, hasLink } from '@ngxp/rest';
import { isEmpty, isNil, isUndefined } from 'lodash-es';
import { BehaviorSubject, Observable, Subject, Subscription, combineLatest, filter, first, map, startWith } from 'rxjs';
@Injectable()
export class BescheidenFormService extends AbstractFormService<CommandResource> implements OnDestroy {
static readonly FIELD_BESCHIEDEN_AM = 'beschiedenAm';
static readonly FIELD_BEWILLIGT = 'bewilligt';
static readonly FIELD_BESCHEID_DOCUMENT = 'bescheidDocument';
static readonly FIELD_ATTACHMENTS = 'attachments';
public static readonly FIELD_SEND_BY = 'sendBy';
static readonly FIELD_NACHRICHT_SUBJECT = 'nachrichtSubject';
static readonly FIELD_NACHRICHT_TEXT = 'nachrichtText';
static readonly FIELD_PATH_PREFIX = 'command.body';
static readonly BETREFF_DEFAULT: string = 'Ihr Bescheid zum Antrag';
private readonly bescheidChanges$: BehaviorSubject<Bescheid>;
private readonly fileDelete$: Subject<BinaryFileResource>;
private readonly showMissingBescheidDocumentError$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
private readonly activeStep$: BehaviorSubject<number> = new BehaviorSubject(1);
readonly sendByManual: BehaviorSubject<boolean> = new BehaviorSubject(false);
vorgangWithEingangResource: VorgangWithEingangResource;
private formControlSubscriptions: Subscription;
private formChangesSubscription: Subscription;
constructor(
formBuilder: UntypedFormBuilder,
private readonly bescheidService: BescheidService,
) {
super(formBuilder);
this.bescheidChanges$ = new BehaviorSubject<Bescheid>(this.getFormValue());
this.fileDelete$ = new Subject<BinaryFileResource>();
this.init();
}
init(): void {
this.formControlSubscriptions = this.subscribeToSendBy();
this.initializeFormChanges();
}
subscribeToSendBy(): Subscription {
return this.form.controls[BescheidenFormService.FIELD_SEND_BY].valueChanges.subscribe((sendBy: BescheidSendBy) =>
this.updateSendByManual(sendBy),
);
}
updateSendByManual(sendBy: BescheidSendBy): void {
const isSendByManual = sendBy === BescheidSendBy.MANUAL;
if (isSendByManual !== this.sendByManual.value) this.sendByManual.next(isSendByManual);
}
initializeFormChanges(): void {
this.formChangesSubscription = this.form.valueChanges
.pipe(
startWith(this.getFormValue()),
map((value) => this.getValueWithApproved(value)),
)
.subscribe((bescheid) => this.bescheidChanges$.next(bescheid));
}
ngOnDestroy(): void {
this.unsubscribe();
}
private unsubscribe(): void {
if (isNotNil(this.formChangesSubscription) && !this.formChangesSubscription.closed) {
this.formChangesSubscription.unsubscribe();
}
if (isNotNil(this.formControlSubscriptions)) this.formControlSubscriptions.unsubscribe();
}
public patchValues(bescheid: BescheidResource): void {
if (isNil(bescheid)) {
return;
}
const bescheidDocumentUri: ResourceUri = this.getBescheidDocumentUri(bescheid);
this.bescheidService.setDocumentUri(bescheidDocumentUri);
this.patch({
[BescheidenFormService.FIELD_BESCHIEDEN_AM]: bescheid.beschiedenAm,
[BescheidenFormService.FIELD_BEWILLIGT]: String(bescheid.bewilligt),
[BescheidenFormService.FIELD_BESCHEID_DOCUMENT]: bescheidDocumentUri,
[BescheidenFormService.FIELD_SEND_BY]: isUndefined(bescheid.sendBy) ? BescheidSendBy.NACHRICHT : bescheid.sendBy,
[BescheidenFormService.FIELD_NACHRICHT_SUBJECT]: bescheid.nachrichtSubject,
[BescheidenFormService.FIELD_NACHRICHT_TEXT]: bescheid.nachrichtText,
});
bescheid.attachments.forEach((attachmentLink) =>
(this.form.controls[BescheidenFormService.FIELD_ATTACHMENTS] as UntypedFormArray).push(
new UntypedFormControl(attachmentLink),
),
);
}
private getBescheidDocumentUri(bescheid: BescheidResource): ResourceUri {
if (hasLink(bescheid, BescheidLinkRel.BESCHEID_DOCUMENT)) {
return getUrl(bescheid, BescheidLinkRel.BESCHEID_DOCUMENT);
}
return null;
}
protected initForm(): UntypedFormGroup {
return this.formBuilder.group({
[BescheidenFormService.FIELD_BESCHIEDEN_AM]: new UntypedFormControl(new Date(Date.now())),
[BescheidenFormService.FIELD_BEWILLIGT]: new UntypedFormControl('true'),
[BescheidenFormService.FIELD_SEND_BY]: new UntypedFormControl(BescheidSendBy.NACHRICHT),
[BescheidenFormService.FIELD_BESCHEID_DOCUMENT]: new UntypedFormControl(null),
[BescheidenFormService.FIELD_ATTACHMENTS]: new UntypedFormArray([]),
[BescheidenFormService.FIELD_NACHRICHT_SUBJECT]: new UntypedFormControl(''),
[BescheidenFormService.FIELD_NACHRICHT_TEXT]: new UntypedFormControl(''),
});
}
protected getPathPrefix(): string {
return BescheidenFormService.FIELD_PATH_PREFIX;
}
protected doSubmit(): Observable<StateResource<CommandResource>> {
if (this.isPatch() || this.bescheidService.existsBescheidDraft()) {
return this.bescheidService.updateBescheid(this.getValue());
} else {
return this.bescheidService
.createBescheid(this.vorgangWithEingangResource, this.getValue())
.pipe(this.updateNachrichtOnSuccess());
}
}
updateNachrichtOnSuccess(): (source: Observable<StateResource<CommandResource>>) => Observable<StateResource<CommandResource>> {
return tapOnCommandSuccessfullyDone(() => {
this.bescheidService
.getBescheidDraft()
.pipe(filter(isLoaded), first())
.subscribe((bescheidStateResource: StateResource<BescheidResource>) => {
this.setNachrichtSubject(bescheidStateResource.resource.nachrichtSubject);
this.setNachrichtText(bescheidStateResource.resource.nachrichtText);
});
});
}
public submitDraft(): Observable<StateResource<Resource>> {
return this.submit().pipe(tapOnCommandSuccessfullyDone(() => this.bescheidService.reloadCurrentVorgang()));
}
public getValue(): Bescheid {
const value: any = this.getFormValue();
return {
...value,
beschiedenAm: formatForDatabase(value.beschiedenAm),
bewilligt: convertToBoolean(value.bewilligt),
attachments: value.attachments,
};
}
public getValueWithApproved(value: any): Bescheid {
return {
...value,
bewilligt: isNotNil(value.bewilligt) ? convertToBoolean(value.bewilligt) : false,
};
}
public getBescheidChanges(): Observable<Bescheid> {
return this.bescheidChanges$.asObservable();
}
public setVorgangWithEingangResource(vorgangWithEingangResource: VorgangWithEingangResource): void {
this.vorgangWithEingangResource = vorgangWithEingangResource;
}
public getVorgangWithEingangResource(): VorgangWithEingangResource {
return this.vorgangWithEingangResource;
}
public deleteFile(binaryFileResource: BinaryFileResource): void {
this.fileDelete$.next(binaryFileResource);
}
public getFileDelete(): Observable<BinaryFileResource> {
return this.fileDelete$.asObservable();
}
public setActiveStep(step: number) {
this.activeStep$.next(step);
}
public getActiveStep(): Observable<number> {
return this.activeStep$.asObservable();
}
public clearBescheidDocumentFile(): void {
this.updateBescheidDocumentFile(null);
}
public updateBescheidDocumentFile(uri: ResourceUri): void {
this.getBescheidDocumentControl().patchValue(uri);
}
private getBescheidDocumentControl(): FormControl {
return <FormControl>this.form.controls[BescheidenFormService.FIELD_BESCHEID_DOCUMENT];
}
public isSendByManual(): Observable<boolean> {
return this.sendByManual.asObservable();
}
public patchNachricht(documentResource: DocumentResource): void {
const value = this.getValue();
if (isEmpty(value.nachrichtSubject)) {
if (isNotEmpty(documentResource.nachrichtSubject)) {
this.setNachrichtSubject(documentResource.nachrichtSubject);
}
}
if (isEmpty(value.nachrichtText)) {
if (isNotEmpty(documentResource.nachrichtText)) {
this.setNachrichtText(documentResource.nachrichtText);
}
}
}
public clearNachricht(): void {
this.setNachrichtSubject(EMPTY_STRING);
this.setNachrichtText(EMPTY_STRING);
}
private setNachrichtSubject(value: string): void {
this.form.controls[BescheidenFormService.FIELD_NACHRICHT_SUBJECT].patchValue(value);
}
private setNachrichtText(value: string): void {
this.form.controls[BescheidenFormService.FIELD_NACHRICHT_TEXT].patchValue(value);
}
public setSendBy(sendBy: BescheidSendBy): void {
this.form.controls[BescheidenFormService.FIELD_SEND_BY].patchValue(sendBy);
}
public getShowMissingBescheidDocumentError(): Observable<boolean> {
return combineLatest([this.showMissingBescheidDocumentError$.asObservable(), this.getBescheidChanges()]).pipe(
map(
([showMissingBescheidDocumentError, bescheid]) => showMissingBescheidDocumentError && isEmpty(bescheid.bescheidDocument),
),
);
}
public validateBescheidDocumentExists(): boolean {
this.showMissingBescheidDocumentError$.next(isEmpty(this.getValue().bescheidDocument));
return isNotEmpty(this.getValue().bescheidDocument);
}
public isBetreffInvalid(): boolean {
return this.form.controls[BescheidenFormService.FIELD_NACHRICHT_SUBJECT].invalid;
}
public isNachrichtInvalid(): boolean {
return this.form.controls[BescheidenFormService.FIELD_NACHRICHT_TEXT].invalid;
}
}
/*
* Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den
* Ministerpräsidenten des Landes Schleswig-Holstein
* Staatskanzlei
* Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
*
* Lizenziert unter der EUPL, Version 1.2 oder - sobald
* diese von der Europäischen Kommission genehmigt wurden -
* Folgeversionen der EUPL ("Lizenz");
* Sie dürfen dieses Werk ausschließlich gemäß
* dieser Lizenz nutzen.
* Eine Kopie der Lizenz finden Sie hier:
*
* https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
*
* Sofern nicht durch anwendbare Rechtsvorschriften
* gefordert oder in schriftlicher Form vereinbart, wird
* die unter der Lizenz verbreitete Software "so wie sie
* ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
* ausdrücklich oder stillschweigend - verbreitet.
* Die sprachspezifischen Genehmigungen und Beschränkungen
* unter der Lizenz sind dem Lizenztext zu entnehmen.
*/
import { BescheidResource } from '@alfa-client/bescheid-shared';
import { VorgangWithEingangResource } from '@alfa-client/vorgang-shared';
export interface BescheidenDialogData {
vorgangWithEingangResource: VorgangWithEingangResource;
bescheidDraftResource?: BescheidResource;
}
<!--
Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den
Ministerpräsidenten des Landes Schleswig-Holstein
Staatskanzlei
Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
Lizenziert unter der EUPL, Version 1.2 oder - sobald
diese von der Europäischen Kommission genehmigt wurden -
Folgeversionen der EUPL ("Lizenz");
Sie dürfen dieses Werk ausschließlich gemäß
dieser Lizenz nutzen.
Eine Kopie der Lizenz finden Sie hier:
https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
Sofern nicht durch anwendbare Rechtsvorschriften
gefordert oder in schriftlicher Form vereinbart, wird
die unter der Lizenz verbreitete Software "so wie sie
ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
ausdrücklich oder stillschweigend - verbreitet.
Die sprachspezifischen Genehmigungen und Beschränkungen
unter der Lizenz sind dem Lizenztext zu entnehmen.
-->
<ods-attachment-wrapper>
<alfa-binary-file2-container
*ngFor="let attachment of existingAttachments"
[file]="attachment"
[deletable]="deletable"
(startDelete)="deleteFile($event)"
>
</alfa-binary-file2-container>
<ng-container *ngFor="let attachment of uploadedAttachments">
<alfa-binary-file2-container
*ngIf="!attachment.loading && attachment.resource"
[file]="attachment.resource"
[deletable]="deletable"
(startDelete)="deleteFile($event)"
[attr.data-test-id]="(attachment.resource.name | convertForDataTest) + '-file2-container'"
>
</alfa-binary-file2-container>
</ng-container>
<ng-container *ngIf="uploadInProgress$ | async as uploadFileInProgress">
<ods-attachment
data-test-id="attachment-upload-in-progress"
*ngIf="uploadFileInProgress.loading || uploadFileInProgress.error"
[loadingCaption]="uploadFileInProgress.fileName"
errorCaption="Fehler beim Hochladen"
[errorMessages]="uploadFileInProgress.error | convertProblemDetailToErrorMessages"
description="Anhang wird hochgeladen"
[isLoading]="uploadFileInProgress.loading"
></ods-attachment>
</ng-container>
</ods-attachment-wrapper>
/*
* Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den
* Ministerpräsidenten des Landes Schleswig-Holstein
* Staatskanzlei
* Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
*
* Lizenziert unter der EUPL, Version 1.2 oder - sobald
* diese von der Europäischen Kommission genehmigt wurden -
* Folgeversionen der EUPL ("Lizenz");
* Sie dürfen dieses Werk ausschließlich gemäß
* dieser Lizenz nutzen.
* Eine Kopie der Lizenz finden Sie hier:
*
* https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
*
* Sofern nicht durch anwendbare Rechtsvorschriften
* gefordert oder in schriftlicher Form vereinbart, wird
* die unter der Lizenz verbreitete Software "so wie sie
* ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
* ausdrücklich oder stillschweigend - verbreitet.
* Die sprachspezifischen Genehmigungen und Beschränkungen
* unter der Lizenz sind dem Lizenztext zu entnehmen.
*/
import { BescheidService } from '@alfa-client/bescheid-shared';
import { BinaryFile2ContainerComponent } from '@alfa-client/binary-file';
import { BinaryFileResource } from '@alfa-client/binary-file-shared';
import {
convertForDataTest,
ConvertForDataTestPipe,
createErrorStateResource,
createStateResource,
FileSizePipe,
StateResource,
} from '@alfa-client/tech-shared';
import { existsAsHtmlElement, Mock, mock, notExistsAsHtmlElement } from '@alfa-client/test-utils';
import { OzgcloudSvgIconComponent, SpinnerComponent } from '@alfa-client/ui';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { MatIcon } from '@angular/material/icon';
import { AttachmentComponent, AttachmentWrapperComponent, SpinnerIconComponent } from '@ods/system';
import { ConvertProblemDetailToErrorMessagesPipe } from 'libs/tech-shared/src/lib/pipe/convert-problem-detail-to-error-messages.pipe';
import { MockComponent, MockPipe } from 'ng-mocks';
import { BehaviorSubject, EMPTY, Observable, of, Subscription } from 'rxjs';
import { createUploadFileInProgress } from '../../../../../../../bescheid-shared/src/test/bescheid';
import {
createBinaryFileResource,
createLoadedBinaryFileResource,
createLoadingBinaryFileStateResource,
} from '../../../../../../../binary-file-shared/test/binary-file';
import { getDataTestIdOf } from '../../../../../../../tech-shared/test/data-test';
import { createApiError } from '../../../../../../../tech-shared/test/error';
import { BescheidenFormService } from '../../bescheiden.formservice';
import { VorgangDetailBescheidenResultAttachmentsComponent } from './vorgang-detail-bescheiden-result-attachments.component';
describe('VorgangDetailBescheidenResultAttachmentsComponent', () => {
let component: VorgangDetailBescheidenResultAttachmentsComponent;
let fixture: ComponentFixture<VorgangDetailBescheidenResultAttachmentsComponent>;
let bescheidService: Mock<BescheidService>;
let formService: Mock<BescheidenFormService>;
beforeEach(async () => {
bescheidService = mock(BescheidService);
bescheidService.getAttachments.mockReturnValue(EMPTY);
bescheidService.getUploadedAttachment.mockReturnValue(EMPTY);
formService = mock(BescheidenFormService);
formService.getBescheidChanges.mockReturnValue(
new BehaviorSubject({ beschiedenAm: new Date(), bewilligt: false }),
);
await TestBed.configureTestingModule({
declarations: [
VorgangDetailBescheidenResultAttachmentsComponent,
ConvertForDataTestPipe,
MatIcon,
MockPipe(FileSizePipe),
MockPipe(ConvertProblemDetailToErrorMessagesPipe),
MockComponent(OzgcloudSvgIconComponent),
MockComponent(SpinnerComponent),
MockComponent(AttachmentWrapperComponent),
MockComponent(BinaryFile2ContainerComponent),
MockComponent(SpinnerIconComponent),
MockComponent(AttachmentComponent),
],
providers: [
{
provide: BescheidenFormService,
useValue: formService,
},
{
provide: BescheidService,
useValue: bescheidService,
},
],
}).compileComponents();
fixture = TestBed.createComponent(VorgangDetailBescheidenResultAttachmentsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
describe('render', () => {
const attachment = createStateResource(createBinaryFileResource());
const dataTestId = getDataTestIdOf(
`${convertForDataTest(attachment.resource.name)}-file2-container`,
);
const uploadInProgressDataTestId: string = getDataTestIdOf('attachment-upload-in-progress');
beforeEach(() => {
component.uploadedAttachments = [attachment];
});
it('should render uploaded file container', () => {
component.ngOnInit();
fixture.detectChanges();
existsAsHtmlElement(fixture, dataTestId);
});
it('should not render uploaded file container on loading', () => {
component.uploadedAttachments = [{ ...attachment, loading: true }];
component.ngOnInit();
fixture.detectChanges();
notExistsAsHtmlElement(fixture, dataTestId);
});
it('should not render uploaded file container on null resource', () => {
component.uploadedAttachments = [{ ...attachment, resource: null }];
component.ngOnInit();
fixture.detectChanges();
notExistsAsHtmlElement(fixture, dataTestId);
});
it('should render attachment in progress component when loading', () => {
component.uploadInProgress$ = of({ ...createUploadFileInProgress(), loading: true });
fixture.detectChanges();
existsAsHtmlElement(fixture, uploadInProgressDataTestId);
});
it('should render attachment in progress component on error', () => {
component.uploadInProgress$ = of({
...createUploadFileInProgress(),
loading: false,
error: createApiError(),
});
fixture.detectChanges();
existsAsHtmlElement(fixture, uploadInProgressDataTestId);
});
it('should not render attachment in progress component when not loading and no error', () => {
component.uploadInProgress$ = of({
...createUploadFileInProgress(),
loading: false,
error: null,
});
fixture.detectChanges();
notExistsAsHtmlElement(fixture, uploadInProgressDataTestId);
});
});
describe('ngOnInit', () => {
let loadExistingAttachments: jest.Mock;
let subscribeToAttachmentUpload: jest.Mock;
beforeEach(() => {
component.loadExistingAttachments = loadExistingAttachments = jest.fn();
component.subscribeToAttachmentUpload = subscribeToAttachmentUpload = jest.fn();
});
it('should load existing attachments', () => {
component.ngOnInit();
expect(loadExistingAttachments).toHaveBeenCalled();
});
it('should subscribe to attachments upload', () => {
component.ngOnInit();
expect(subscribeToAttachmentUpload).toHaveBeenCalled();
});
it('should get upload attachment in progress', () => {
component.ngOnInit();
expect(bescheidService.getUploadAttachmentInProgress).toHaveBeenCalled();
});
});
describe('ngOnDestroy', () => {
it('should unsubscribe', () => {
const subscription = mock(Subscription);
const observable = mock(Observable);
observable.subscribe.mockReturnValue(subscription);
bescheidService.getUploadedAttachment.mockReturnValue(observable);
component.ngOnInit();
component.ngOnDestroy();
expect(subscription.unsubscribe).toHaveBeenCalled();
});
});
describe('loadExistingAttachments', () => {
let attachment: BinaryFileResource;
beforeEach(() => {
attachment = createBinaryFileResource();
bescheidService.getAttachments.mockReturnValue(of([attachment]));
});
it('should get attachments', () => {
component.loadExistingAttachments();
expect(bescheidService.getAttachments).toHaveBeenCalled();
});
it('should set existing attachments', () => {
component.loadExistingAttachments();
expect(component.existingAttachments).toEqual([attachment]);
});
it('should reset uploaded attachments', () => {
component.uploadedAttachments = [createStateResource(createBinaryFileResource())];
component.loadExistingAttachments();
expect(component.uploadedAttachments).toEqual([]);
});
});
describe('subscribeToAttachmentUpload', () => {
let attachment: StateResource<BinaryFileResource>;
beforeEach(() => {
attachment = createLoadedBinaryFileResource();
bescheidService.getUploadedAttachment.mockReturnValue(of(attachment));
});
it('should should set uploaded attachments', () => {
component.buildUploadedAttachments = jest.fn().mockReturnValue([attachment]);
component.subscribeToAttachmentUpload();
expect(component.uploadedAttachments).toEqual([attachment]);
});
});
describe('buildUploadedAttachments', () => {
let uploadedAttachment: StateResource<BinaryFileResource>;
let uploadedAttachments: StateResource<BinaryFileResource>[];
let loadingAttachment: StateResource<BinaryFileResource>;
beforeEach(() => {
uploadedAttachment = createLoadedBinaryFileResource();
uploadedAttachments = [uploadedAttachment];
component.uploadedAttachments = uploadedAttachments;
loadingAttachment = createLoadingBinaryFileStateResource();
});
it('should add loaded state resource', () => {
const newBinaryFileResource = createLoadedBinaryFileResource();
const uploaded = component.buildUploadedAttachments(newBinaryFileResource);
expect(uploaded).toEqual([uploadedAttachment, newBinaryFileResource]);
});
it('should add error state resource', () => {
const errorStateResource = createErrorStateResource(createApiError());
const uploaded = component.buildUploadedAttachments(errorStateResource);
expect(uploaded).toEqual([uploadedAttachment, errorStateResource]);
});
it('should add loading state resource if none exists', () => {
const uploaded = component.buildUploadedAttachments(loadingAttachment);
expect(uploaded).toEqual([uploadedAttachment, loadingAttachment]);
});
it('should not add loading state resource if one exists', () => {
component.uploadedAttachments = [...component.uploadedAttachments, loadingAttachment];
const uploaded = component.buildUploadedAttachments(createLoadingBinaryFileStateResource());
expect(uploaded).toEqual([uploadedAttachment, loadingAttachment]);
});
it('should should not have error state resource if loading in progress', () => {
component.uploadedAttachments = [createErrorStateResource(createApiError())];
const uploaded = component.buildUploadedAttachments(loadingAttachment);
expect(uploaded).toEqual([loadingAttachment]);
});
});
describe('deleteFile', () => {
let existingAttachment: BinaryFileResource;
let uploadedAttachmentStateResource: StateResource<BinaryFileResource>;
beforeEach(() => {
existingAttachment = createBinaryFileResource();
uploadedAttachmentStateResource = createStateResource(createBinaryFileResource());
});
it('should call form service', () => {
const file = createBinaryFileResource();
component.deleteFile(file);
expect(formService.deleteFile).toHaveBeenCalledWith(file);
});
it('should delete attachment from uploaded', () => {
component.uploadedAttachments = [uploadedAttachmentStateResource];
component.deleteFile(uploadedAttachmentStateResource.resource);
expect(component.uploadedAttachments).toEqual([]);
});
it('should leave errors in uploaded', () => {
const error: StateResource<BinaryFileResource> = createStateResource(
createBinaryFileResource(),
);
component.uploadedAttachments = [error];
component.deleteFile(uploadedAttachmentStateResource.resource);
expect(component.uploadedAttachments).toEqual([error]);
});
it('should not delete attachment from existing', () => {
component.uploadedAttachments = [uploadedAttachmentStateResource];
component.existingAttachments = [existingAttachment];
component.deleteFile(uploadedAttachmentStateResource.resource);
expect(component.existingAttachments).toEqual([existingAttachment]);
});
it('should delete attachment from existing', () => {
component.existingAttachments = [existingAttachment];
component.deleteFile(existingAttachment);
expect(component.existingAttachments).toEqual([]);
});
it('should not delete attachment from existing', () => {
component.uploadedAttachments = [uploadedAttachmentStateResource];
component.existingAttachments = [existingAttachment];
component.deleteFile(existingAttachment);
expect(component.uploadedAttachments).toEqual([uploadedAttachmentStateResource]);
});
});
});
/*
* Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den
* Ministerpräsidenten des Landes Schleswig-Holstein
* Staatskanzlei
* Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
*
* Lizenziert unter der EUPL, Version 1.2 oder - sobald
* diese von der Europäischen Kommission genehmigt wurden -
* Folgeversionen der EUPL ("Lizenz");
* Sie dürfen dieses Werk ausschließlich gemäß
* dieser Lizenz nutzen.
* Eine Kopie der Lizenz finden Sie hier:
*
* https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
*
* Sofern nicht durch anwendbare Rechtsvorschriften
* gefordert oder in schriftlicher Form vereinbart, wird
* die unter der Lizenz verbreitete Software "so wie sie
* ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
* ausdrücklich oder stillschweigend - verbreitet.
* Die sprachspezifischen Genehmigungen und Beschränkungen
* unter der Lizenz sind dem Lizenztext zu entnehmen.
*/
import { BescheidService, UploadFileInProgress } from '@alfa-client/bescheid-shared';
import { BinaryFileResource } from '@alfa-client/binary-file-shared';
import {
containsLoading,
getSuccessfullyLoaded,
hasStateResourceError,
isLoaded,
isNotNil,
StateResource,
} from '@alfa-client/tech-shared';
import { Component, Input, OnDestroy, OnInit } from '@angular/core';
import { getUrl } from '@ngxp/rest';
import { first, Observable, Subscription } from 'rxjs';
import { BescheidenFormService } from '../../bescheiden.formservice';
@Component({
selector: 'alfa-vorgang-detail-bescheiden-result-attachments',
templateUrl: './vorgang-detail-bescheiden-result-attachments.component.html',
styles: [],
})
export class VorgangDetailBescheidenResultAttachmentsComponent implements OnDestroy, OnInit {
@Input() public deletable: boolean;
existingAttachments: BinaryFileResource[] = [];
uploadedAttachments: StateResource<BinaryFileResource>[] = [];
uploadInProgress$: Observable<UploadFileInProgress>;
private attachmentUploadSubscription: Subscription;
constructor(
public formService: BescheidenFormService,
private readonly bescheidService: BescheidService,
) {}
ngOnInit(): void {
this.uploadInProgress$ = this.bescheidService.getUploadAttachmentInProgress();
this.loadExistingAttachments();
this.subscribeToAttachmentUpload();
}
ngOnDestroy(): void {
if (isNotNil(this.attachmentUploadSubscription))
this.attachmentUploadSubscription.unsubscribe();
}
loadExistingAttachments() {
this.bescheidService
.getAttachments()
.pipe(first())
.subscribe((attachments) => {
this.uploadedAttachments = [];
this.existingAttachments = attachments;
});
}
subscribeToAttachmentUpload() {
this.attachmentUploadSubscription = this.bescheidService
.getUploadedAttachment()
.subscribe(
(stateResource: StateResource<BinaryFileResource>) =>
(this.uploadedAttachments = this.buildUploadedAttachments(stateResource)),
);
}
buildUploadedAttachments(
uploadStateResource: StateResource<BinaryFileResource>,
): StateResource<BinaryFileResource>[] {
if (isLoaded(uploadStateResource) || hasStateResourceError(uploadStateResource)) {
return [...getSuccessfullyLoaded(this.uploadedAttachments), uploadStateResource];
} else if (uploadStateResource.loading && !containsLoading(this.uploadedAttachments)) {
return [...getSuccessfullyLoaded(this.uploadedAttachments), uploadStateResource];
}
return this.uploadedAttachments;
}
deleteFile(file: BinaryFileResource): void {
this.formService.deleteFile(file);
this.uploadedAttachments = this.uploadedAttachments.filter(
(attachment: StateResource<BinaryFileResource>) =>
!hasStateResourceError(attachment) && getUrl(attachment.resource) !== getUrl(file),
);
this.existingAttachments = this.existingAttachments.filter(
(attachment: BinaryFileResource) => getUrl(attachment) !== getUrl(file),
);
}
}
<!--
Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den
Ministerpräsidenten des Landes Schleswig-Holstein
Staatskanzlei
Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
Lizenziert unter der EUPL, Version 1.2 oder - sobald
diese von der Europäischen Kommission genehmigt wurden -
Folgeversionen der EUPL ("Lizenz");
Sie dürfen dieses Werk ausschließlich gemäß
dieser Lizenz nutzen.
Eine Kopie der Lizenz finden Sie hier:
https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
Sofern nicht durch anwendbare Rechtsvorschriften
gefordert oder in schriftlicher Form vereinbart, wird
die unter der Lizenz verbreitete Software "so wie sie
ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
ausdrücklich oder stillschweigend - verbreitet.
Die sprachspezifischen Genehmigungen und Beschränkungen
unter der Lizenz sind dem Lizenztext zu entnehmen.
-->
<p
*ngIf="showMissingBescheidDocumentError"
data-test-id="missing-bescheid-document-error-message"
class="text-base text-error"
>
Bitte fügen Sie ein Bescheiddokument hinzu.
</p>
<ods-attachment-wrapper>
<ng-container *ngIf="bescheidDocumentFile.resource">
<alfa-binary-file2-container
*ngIf="
!bescheidDocumentFile.loading &&
!uploadBescheidDocumentInProgress.loading &&
!createDocumentInProgess.loading
"
[file]="bescheidDocumentFile.resource"
[deletable]="deletable"
(startDelete)="deleteFile.emit()"
>
</alfa-binary-file2-container>
</ng-container>
<ods-attachment
*ngIf="uploadBescheidDocumentInProgress.loading || uploadBescheidDocumentInProgress.error"
errorCaption="Fehler beim Hochladen"
[loadingCaption]="uploadBescheidDocumentInProgress.fileName"
[attr.data-test-id]="
'upload-bescheid-document-error-' + !!uploadBescheidDocumentInProgress.error
"
[isLoading]="uploadBescheidDocumentInProgress.loading"
[errorMessages]="uploadBescheidDocumentInProgress.error | convertProblemDetailToErrorMessages"
description="Bescheiddokument wird hochgeladen"
></ods-attachment>
<ods-attachment
*ngIf="createDocumentInProgess.loading || hasCreateDocumentError"
errorCaption="Fehler beim automatischen Erstellen"
loadingCaption="Bescheiddokument"
description="Bescheiddokument wird erstellt"
[isLoading]="createDocumentInProgess.loading"
[errorMessages]="createDocumentErrorMessages"
data-test-id="create-bescheid-document-attachment"
></ods-attachment>
</ods-attachment-wrapper>
/*
* Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den
* Ministerpräsidenten des Landes Schleswig-Holstein
* Staatskanzlei
* Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
*
* Lizenziert unter der EUPL, Version 1.2 oder - sobald
* diese von der Europäischen Kommission genehmigt wurden -
* Folgeversionen der EUPL ("Lizenz");
* Sie dürfen dieses Werk ausschließlich gemäß
* dieser Lizenz nutzen.
* Eine Kopie der Lizenz finden Sie hier:
*
* https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
*
* Sofern nicht durch anwendbare Rechtsvorschriften
* gefordert oder in schriftlicher Form vereinbart, wird
* die unter der Lizenz verbreitete Software "so wie sie
* ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
* ausdrücklich oder stillschweigend - verbreitet.
* Die sprachspezifischen Genehmigungen und Beschränkungen
* unter der Lizenz sind dem Lizenztext zu entnehmen.
*/
import { BescheidLinkRel, BescheidResource, BescheidService } from '@alfa-client/bescheid-shared';
import { BinaryFile2ContainerComponent } from '@alfa-client/binary-file';
import { CommandResource } from '@alfa-client/command-shared';
import {
StateResource,
createEmptyStateResource,
createStateResource,
} from '@alfa-client/tech-shared';
import { Mock, existsAsHtmlElement, mock, notExistsAsHtmlElement } from '@alfa-client/test-utils';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { getUrl } from '@ngxp/rest';
import { AttachmentComponent, AttachmentWrapperComponent } from '@ods/system';
import { createBescheidResource } from 'libs/bescheid-shared/src/test/bescheid';
import { createBinaryFileResource } from 'libs/binary-file-shared/test/binary-file';
import { ConvertProblemDetailToErrorMessagesPipe } from 'libs/tech-shared/src/lib/pipe/convert-problem-detail-to-error-messages.pipe';
import { getDataTestIdOf } from 'libs/tech-shared/test/data-test';
import { createApiError } from 'libs/tech-shared/test/error';
import { MockComponent, MockPipe } from 'ng-mocks';
import {
createCommandErrorStateResource,
createCommandResource,
createCommandStateResource,
} from '../../../../../../../command-shared/test/command';
import { VorgangDetailBescheidenResultDokumentComponent } from './vorgang-detail-bescheiden-result-dokument.component';
describe('VorgangDetailBescheidenResultDokumentComponent', () => {
let component: VorgangDetailBescheidenResultDokumentComponent;
let fixture: ComponentFixture<VorgangDetailBescheidenResultDokumentComponent>;
const createBescheidDocumentAttachment: string = getDataTestIdOf(
'create-bescheid-document-attachment',
);
const uploadBescheidDocumentError: string = getDataTestIdOf(
'upload-bescheid-document-error-true',
);
const missingBescheidDocumentErrorMessage: string = getDataTestIdOf(
'missing-bescheid-document-error-message',
);
let bescheidService: Mock<BescheidService>;
beforeEach(async () => {
bescheidService = mock(BescheidService);
await TestBed.configureTestingModule({
declarations: [
VorgangDetailBescheidenResultDokumentComponent,
MockComponent(BinaryFile2ContainerComponent),
MockComponent(AttachmentComponent),
MockComponent(AttachmentWrapperComponent),
MockPipe(ConvertProblemDetailToErrorMessagesPipe),
],
providers: [
{
provide: BescheidService,
useValue: bescheidService,
},
],
}).compileComponents();
fixture = TestBed.createComponent(VorgangDetailBescheidenResultDokumentComponent);
component = fixture.componentInstance;
component.bescheidDocumentFile = createStateResource(createBinaryFileResource());
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
describe('render', () => {
it('should show error message for missing bescheid document', () => {
component.showMissingBescheidDocumentError = true;
fixture.detectChanges();
existsAsHtmlElement(fixture, missingBescheidDocumentErrorMessage);
});
it('should not show error message for missing bescheid document', () => {
component.showMissingBescheidDocumentError = false;
fixture.detectChanges();
notExistsAsHtmlElement(fixture, missingBescheidDocumentErrorMessage);
});
describe('create bescheid document ods-attachment', () => {
it('should be shown if error exists', () => {
component.createBescheidDocumentInProgress = createCommandErrorStateResource();
fixture.detectChanges();
existsAsHtmlElement(fixture, createBescheidDocumentAttachment);
});
it('should be shown while loading', () => {
component.createBescheidDocumentInProgress = createStateResource(
createCommandResource(),
true,
);
fixture.detectChanges();
existsAsHtmlElement(fixture, createBescheidDocumentAttachment);
});
it('should be hidden on loaded without error', () => {
component.createBescheidDocumentInProgress = createCommandStateResource();
fixture.detectChanges();
notExistsAsHtmlElement(fixture, createBescheidDocumentAttachment);
});
});
});
describe('handle bescheid document', () => {
it('should call service to load bescheid document by uri if link is present', () => {
const bescheid: BescheidResource = createBescheidResource([
BescheidLinkRel.BESCHEID_DOCUMENT,
]);
component.handleBescheidDocument(bescheid);
expect(bescheidService.loadBescheidDocumentByUri).toHaveBeenCalledWith(
getUrl(bescheid, BescheidLinkRel.BESCHEID_DOCUMENT),
);
});
it('should NOT call service if link is missing', () => {
component.handleBescheidDocument(createBescheidResource());
expect(bescheidService.loadBescheidDocumentByUri).not.toHaveBeenCalled();
});
});
describe('upload bescheid document error', () => {
it('should be shown if error exists', () => {
component.uploadBescheidDocumentInProgress = { loading: false, error: createApiError() };
fixture.detectChanges();
existsAsHtmlElement(fixture, uploadBescheidDocumentError);
});
it('should be hidden on non error', () => {
component.uploadBescheidDocumentInProgress = { loading: false };
fixture.detectChanges();
notExistsAsHtmlElement(fixture, uploadBescheidDocumentError);
});
});
describe('set create bescheid document in progress', () => {
beforeEach(() => {
component.handleCreateBescheidDocumentCommandError = jest.fn();
});
it('should set create document in progress', () => {
const commandStateResource: StateResource<CommandResource> = createEmptyStateResource();
component.createBescheidDocumentInProgress = commandStateResource;
expect(component.createDocumentInProgess).toBe(commandStateResource);
});
it('should handle command error', () => {
const commandStateResource: StateResource<CommandResource> = createEmptyStateResource();
component.createBescheidDocumentInProgress = commandStateResource;
expect(component.handleCreateBescheidDocumentCommandError).toHaveBeenCalledWith(
commandStateResource,
);
});
});
describe('handleCreateBescheidDocumentCommandError', () => {
it('should set has error', () => {
component.handleCreateBescheidDocumentCommandError(createCommandErrorStateResource());
expect(component.hasCreateDocumentError).toBeTruthy();
});
it('should not set has error', () => {
component.handleCreateBescheidDocumentCommandError(createCommandStateResource());
expect(component.hasCreateDocumentError).toBeFalsy();
});
it('should not set has error on empty state resource', () => {
component.handleCreateBescheidDocumentCommandError(createEmptyStateResource());
expect(component.hasCreateDocumentError).toBeFalsy();
});
it('should set error messages', () => {
component.handleCreateBescheidDocumentCommandError(createCommandErrorStateResource());
expect(component.createDocumentErrorMessages).toEqual([
VorgangDetailBescheidenResultDokumentComponent.CREATE_DOCUMENT_ERROR_MESSAGE,
]);
});
it('should not set error messages', () => {
component.handleCreateBescheidDocumentCommandError(createCommandStateResource());
expect(component.createDocumentErrorMessages).toEqual([]);
});
});
});
/*
* Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den
* Ministerpräsidenten des Landes Schleswig-Holstein
* Staatskanzlei
* Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
*
* Lizenziert unter der EUPL, Version 1.2 oder - sobald
* diese von der Europäischen Kommission genehmigt wurden -
* Folgeversionen der EUPL ("Lizenz");
* Sie dürfen dieses Werk ausschließlich gemäß
* dieser Lizenz nutzen.
* Eine Kopie der Lizenz finden Sie hier:
*
* https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
*
* Sofern nicht durch anwendbare Rechtsvorschriften
* gefordert oder in schriftlicher Form vereinbart, wird
* die unter der Lizenz verbreitete Software "so wie sie
* ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
* ausdrücklich oder stillschweigend - verbreitet.
* Die sprachspezifischen Genehmigungen und Beschränkungen
* unter der Lizenz sind dem Lizenztext zu entnehmen.
*/
import {
BescheidLinkRel,
BescheidResource,
BescheidService,
UploadFileInProgress,
} from '@alfa-client/bescheid-shared';
import { BinaryFileResource } from '@alfa-client/binary-file-shared';
import { CommandResource, hasCommandError } from '@alfa-client/command-shared';
import { createEmptyStateResource, isNotNil, StateResource } from '@alfa-client/tech-shared';
import { Component, EventEmitter, Input, Output } from '@angular/core';
import { getUrl, hasLink } from '@ngxp/rest';
@Component({
selector: 'alfa-vorgang-detail-bescheiden-result-dokument',
templateUrl: './vorgang-detail-bescheiden-result-dokument.component.html',
})
export class VorgangDetailBescheidenResultDokumentComponent {
static readonly CREATE_DOCUMENT_ERROR_MESSAGE: string =
'Bescheiddokument konnte nicht erzeugt werden.';
@Input() bescheidDocumentFile: StateResource<BinaryFileResource>;
@Input() uploadBescheidDocumentInProgress: UploadFileInProgress = { loading: false };
@Input() set createBescheidDocumentInProgress(
commandStateResource: StateResource<CommandResource>,
) {
this.createDocumentInProgess = commandStateResource;
this.handleCreateBescheidDocumentCommandError(commandStateResource);
}
@Input() set bescheidDraftStateResource(bescheidStateResource: StateResource<BescheidResource>) {
this.handleBescheidDocument(bescheidStateResource.resource);
}
@Input() public deletable: boolean;
@Input() public showMissingBescheidDocumentError: boolean;
@Output() deleteFile: EventEmitter<void> = new EventEmitter<void>();
createDocumentErrorMessages: string[] = [];
createDocumentInProgess: StateResource<CommandResource> = createEmptyStateResource();
hasCreateDocumentError: boolean = false;
constructor(private bescheidService: BescheidService) {}
handleBescheidDocument(bescheid: BescheidResource): void {
if (hasLink(bescheid, BescheidLinkRel.BESCHEID_DOCUMENT)) {
this.bescheidService.loadBescheidDocumentByUri(
getUrl(bescheid, BescheidLinkRel.BESCHEID_DOCUMENT),
);
}
}
handleCreateBescheidDocumentCommandError(
commandStateResource: StateResource<CommandResource>,
): void {
this.hasCreateDocumentError =
isNotNil(commandStateResource.resource) && hasCommandError(commandStateResource.resource);
if (this.hasCreateDocumentError) {
this.createDocumentErrorMessages = [
VorgangDetailBescheidenResultDokumentComponent.CREATE_DOCUMENT_ERROR_MESSAGE,
];
} else {
this.createDocumentErrorMessages = [];
}
}
}
<!--
Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den
Ministerpräsidenten des Landes Schleswig-Holstein
Staatskanzlei
Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
Lizenziert unter der EUPL, Version 1.2 oder - sobald
diese von der Europäischen Kommission genehmigt wurden -
Folgeversionen der EUPL ("Lizenz");
Sie dürfen dieses Werk ausschließlich gemäß
dieser Lizenz nutzen.
Eine Kopie der Lizenz finden Sie hier:
https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
Sofern nicht durch anwendbare Rechtsvorschriften
gefordert oder in schriftlicher Form vereinbart, wird
die unter der Lizenz verbreitete Software "so wie sie
ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
ausdrücklich oder stillschweigend - verbreitet.
Die sprachspezifischen Genehmigungen und Beschränkungen
unter der Lizenz sind dem Lizenztext zu entnehmen.
-->
<h3 class="mb-3 font-medium text-primary">Neue Nachricht</h3>
<p
*ngIf="empfaenger$ | async as empfaenger"
data-test-id="bescheid-nachricht-empfaenger"
class="mb-2 text-sm font-medium"
>
An: {{ empfaenger }}
</p>
<div [formGroup]="form">
<ods-text-editor
[formControlName]="formServiceClass.FIELD_NACHRICHT_SUBJECT"
label="Betreff"
placeholder="Betreff hier eingeben"
[focus]="focusBetreff"
[isRequired]="true"
>
</ods-text-editor>
<ods-textarea-editor
[formControlName]="formServiceClass.FIELD_NACHRICHT_TEXT"
label="Text"
placeholder="Nachrichtentext hier eingeben"
[focus]="focusNachricht"
[isRequired]="true"
>
</ods-textarea-editor>
</div>
/*
* Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den
* Ministerpräsidenten des Landes Schleswig-Holstein
* Staatskanzlei
* Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
*
* Lizenziert unter der EUPL, Version 1.2 oder - sobald
* diese von der Europäischen Kommission genehmigt wurden -
* Folgeversionen der EUPL ("Lizenz");
* Sie dürfen dieses Werk ausschließlich gemäß
* dieser Lizenz nutzen.
* Eine Kopie der Lizenz finden Sie hier:
*
* https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
*
* Sofern nicht durch anwendbare Rechtsvorschriften
* gefordert oder in schriftlicher Form vereinbart, wird
* die unter der Lizenz verbreitete Software "so wie sie
* ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
* ausdrücklich oder stillschweigend - verbreitet.
* Die sprachspezifischen Genehmigungen und Beschränkungen
* unter der Lizenz sind dem Lizenztext zu entnehmen.
*/
import { BescheidService, DocumentResource } from '@alfa-client/bescheid-shared';
import {
createEmptyStateResource,
createErrorStateResource,
createStateResource,
StateResource,
} from '@alfa-client/tech-shared';
import { getElementFromFixture } from '@alfa-client/test-utils';
import { registerLocaleData } from '@angular/common';
import localeDe from '@angular/common/locales/de';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ReactiveFormsModule, UntypedFormBuilder } from '@angular/forms';
import { faker } from '@faker-js/faker';
import { TextareaEditorComponent, TextEditorComponent } from '@ods/component';
import { Mock, mock, useFromMock } from 'libs/test-utils/src/lib/mocking';
import { OzgcloudSvgIconComponent } from 'libs/ui/src/lib/ui/ozgcloud-svgicon/ozgcloud-svgicon.component';
import { MockComponent } from 'ng-mocks';
import { EMPTY, of } from 'rxjs';
import { createDocumentResource } from '../../../../../../../bescheid-shared/src/test/document';
import { getDataTestIdOf } from '../../../../../../../tech-shared/test/data-test';
import { createApiError } from '../../../../../../../tech-shared/test/error';
import { singleColdCompleted } from '../../../../../../../tech-shared/test/marbles';
import { BescheidenFormService } from '../../bescheiden.formservice';
import { VorgangDetailBescheidenResultNachrichtComponent } from './vorgang-detail-bescheiden-result-nachricht.component';
registerLocaleData(localeDe);
describe('VorgangDetailBescheidenResultNachrichtComponent', () => {
let component: VorgangDetailBescheidenResultNachrichtComponent;
let fixture: ComponentFixture<VorgangDetailBescheidenResultNachrichtComponent>;
let bescheidService: Mock<BescheidService>;
let formService: BescheidenFormService;
const bescheidNachrichtEmpfaengerElement: string = getDataTestIdOf(
'bescheid-nachricht-empfaenger',
);
beforeEach(async () => {
bescheidService = mock(BescheidService);
formService = new BescheidenFormService(new UntypedFormBuilder(), useFromMock(bescheidService));
await TestBed.configureTestingModule({
imports: [ReactiveFormsModule],
declarations: [
VorgangDetailBescheidenResultNachrichtComponent,
MockComponent(OzgcloudSvgIconComponent),
MockComponent(TextEditorComponent),
MockComponent(TextareaEditorComponent),
],
providers: [
{
provide: BescheidService,
useValue: bescheidService,
},
{
provide: BescheidenFormService,
useValue: formService,
},
],
}).compileComponents();
fixture = TestBed.createComponent(VorgangDetailBescheidenResultNachrichtComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
describe('render', () => {
it('should render Nachrichtenempfänger', () => {
const empfaenger: string = `${faker.person.firstName()} ${faker.person.lastName()}`;
bescheidService.getEmpfaenger.mockReturnValue(of(empfaenger));
component.ngOnInit();
fixture.detectChanges();
const element = getElementFromFixture(fixture, bescheidNachrichtEmpfaengerElement);
expect(element).toBeInstanceOf(HTMLElement);
});
it('should not render Nachrichtenempfänger', () => {
bescheidService.getEmpfaenger.mockReturnValue(EMPTY);
component.ngOnInit();
fixture.detectChanges();
const element = getElementFromFixture(fixture, bescheidNachrichtEmpfaengerElement);
expect(element).not.toBeInstanceOf(HTMLElement);
});
});
describe('ngOnInit', () => {
it('should set Nachrichtenempfänger', () => {
const empfaenger: string = `${faker.person.firstName()} ${faker.person.lastName()}`;
bescheidService.getEmpfaenger.mockReturnValue(of(empfaenger));
component.ngOnInit();
expect(component.empfaenger$).toBeObservable(singleColdCompleted(empfaenger));
});
});
describe('set bescheidDocumentStateResource', () => {
beforeEach(() => {
formService.patchNachricht = jest.fn();
});
it('should patch form', () => {
const documentStateResource: StateResource<DocumentResource> =
createStateResource(createDocumentResource());
jest.spyOn(formService.form, 'valid', 'get').mockReturnValue(true);
component.bescheidDocumentStateResource = documentStateResource;
expect(formService.patchNachricht).toHaveBeenCalledWith(documentStateResource.resource);
});
it('should not patch if form is invalid', () => {
const documentStateResource: StateResource<DocumentResource> =
createStateResource(createDocumentResource());
jest.spyOn(formService.form, 'valid', 'get').mockReturnValue(false);
component.bescheidDocumentStateResource = documentStateResource;
expect(formService.patchNachricht).not.toHaveBeenCalledWith(documentStateResource.resource);
});
it('should not patch form if document loading', () => {
component.bescheidDocumentStateResource = { ...createEmptyStateResource(), loading: true };
expect(formService.patchNachricht).not.toHaveBeenCalled();
});
it('should not patch form if document loaded with error', () => {
component.bescheidDocumentStateResource = createErrorStateResource(createApiError());
expect(formService.patchNachricht).not.toHaveBeenCalled();
});
it.each([null, undefined])(
'should not patch form if document resource is %s',
(resource: DocumentResource) => {
component.bescheidDocumentStateResource = createStateResource(resource);
expect(formService.patchNachricht).not.toHaveBeenCalled();
},
);
});
describe('resetFocus', () => {
it('should reset betreff focus', () => {
component.focusBetreff = true;
component.resetFocus();
expect(component.focusBetreff).toBeFalsy();
});
it('should reset nachricht focus', () => {
component.focusNachricht = true;
component.resetFocus();
expect(component.focusNachricht).toBeFalsy();
});
});
describe('sendWithNachrichtCommandStateResource', () => {
beforeEach(() => {
component.resetFocus = jest.fn();
});
it('should reset focus', () => {
component.sendWithNachrichtCommandStateResource = createEmptyStateResource();
expect(component.resetFocus).toHaveBeenCalled();
});
it('should focus betreff', () => {
component.focusBetreff = false;
formService.isBetreffInvalid = jest.fn().mockReturnValue(true);
formService.isNachrichtInvalid = jest.fn().mockReturnValue(false);
component.sendWithNachrichtCommandStateResource = createErrorStateResource(createApiError());
expect(component.focusBetreff).toBeTruthy();
});
it('should focus nachricht', () => {
component.focusNachricht = false;
formService.isBetreffInvalid = jest.fn().mockReturnValue(false);
formService.isNachrichtInvalid = jest.fn().mockReturnValue(true);
component.sendWithNachrichtCommandStateResource = createErrorStateResource(createApiError());
expect(component.focusNachricht).toBeTruthy();
});
it('should focus betreff first', () => {
component.focusNachricht = false;
component.focusBetreff = false;
formService.isBetreffInvalid = jest.fn().mockReturnValue(true);
formService.isNachrichtInvalid = jest.fn().mockReturnValue(true);
component.sendWithNachrichtCommandStateResource = createErrorStateResource(createApiError());
expect(component.focusBetreff).toBeTruthy();
expect(component.focusNachricht).toBeFalsy();
});
});
});
/*
* Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den
* Ministerpräsidenten des Landes Schleswig-Holstein
* Staatskanzlei
* Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
*
* Lizenziert unter der EUPL, Version 1.2 oder - sobald
* diese von der Europäischen Kommission genehmigt wurden -
* Folgeversionen der EUPL ("Lizenz");
* Sie dürfen dieses Werk ausschließlich gemäß
* dieser Lizenz nutzen.
* Eine Kopie der Lizenz finden Sie hier:
*
* https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
*
* Sofern nicht durch anwendbare Rechtsvorschriften
* gefordert oder in schriftlicher Form vereinbart, wird
* die unter der Lizenz verbreitete Software "so wie sie
* ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
* ausdrücklich oder stillschweigend - verbreitet.
* Die sprachspezifischen Genehmigungen und Beschränkungen
* unter der Lizenz sind dem Lizenztext zu entnehmen.
*/
import { BescheidService, DocumentResource } from '@alfa-client/bescheid-shared';
import { CommandResource } from '@alfa-client/command-shared';
import { hasStateResourceError, isNotNil, StateResource } from '@alfa-client/tech-shared';
import { Component, Input, OnInit } from '@angular/core';
import { FormGroup } from '@angular/forms';
import { Observable } from 'rxjs';
import { BescheidenFormService } from '../../bescheiden.formservice';
@Component({
selector: 'alfa-vorgang-detail-bescheiden-result-nachricht',
templateUrl: './vorgang-detail-bescheiden-result-nachricht.component.html',
})
export class VorgangDetailBescheidenResultNachrichtComponent implements OnInit {
@Input()
set bescheidDocumentStateResource(stateResource: StateResource<DocumentResource>) {
if (!stateResource.loading && !stateResource.error && isNotNil(stateResource.resource)) {
if (isNotNil(this.form) && this.form.valid) {
this.formService.patchNachricht(stateResource.resource);
}
}
}
@Input()
set sendWithNachrichtCommandStateResource(stateResource: StateResource<CommandResource>) {
this.resetFocus();
if (isNotNil(stateResource) && hasStateResourceError(stateResource)) {
if (this.formService.isBetreffInvalid()) {
this.focusBetreff = true;
} else if (this.formService.isNachrichtInvalid()) {
this.focusNachricht = true;
}
}
}
resetFocus() {
this.focusBetreff = false;
this.focusNachricht = false;
}
empfaenger$: Observable<string>;
form: FormGroup;
public focusBetreff: boolean = false;
public focusNachricht: boolean = false;
readonly formServiceClass = BescheidenFormService;
constructor(
private readonly bescheidService: BescheidService,
private readonly formService: BescheidenFormService,
) {}
ngOnInit(): void {
this.empfaenger$ = this.bescheidService.getEmpfaenger();
this.form = this.formService.form;
}
}
<!--
Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den
Ministerpräsidenten des Landes Schleswig-Holstein
Staatskanzlei
Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
Lizenziert unter der EUPL, Version 1.2 oder - sobald
diese von der Europäischen Kommission genehmigt wurden -
Folgeversionen der EUPL ("Lizenz");
Sie dürfen dieses Werk ausschließlich gemäß
dieser Lizenz nutzen.
Eine Kopie der Lizenz finden Sie hier:
https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
Sofern nicht durch anwendbare Rechtsvorschriften
gefordert oder in schriftlicher Form vereinbart, wird
die unter der Lizenz verbreitete Software "so wie sie
ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
ausdrücklich oder stillschweigend - verbreitet.
Die sprachspezifischen Genehmigungen und Beschränkungen
unter der Lizenz sind dem Lizenztext zu entnehmen.
-->
<ods-bescheid-status-text
[bewilligt]="bescheid.bewilligt"
[dateText]="bescheid.beschiedenAm | date: 'dd.MM.yyyy'"
[hasBescheidDraft]="false"
></ods-bescheid-status-text>
/*
* Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den
* Ministerpräsidenten des Landes Schleswig-Holstein
* Staatskanzlei
* Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
*
* Lizenziert unter der EUPL, Version 1.2 oder - sobald
* diese von der Europäischen Kommission genehmigt wurden -
* Folgeversionen der EUPL ("Lizenz");
* Sie dürfen dieses Werk ausschließlich gemäß
* dieser Lizenz nutzen.
* Eine Kopie der Lizenz finden Sie hier:
*
* https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
*
* Sofern nicht durch anwendbare Rechtsvorschriften
* gefordert oder in schriftlicher Form vereinbart, wird
* die unter der Lizenz verbreitete Software "so wie sie
* ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
* ausdrücklich oder stillschweigend - verbreitet.
* Die sprachspezifischen Genehmigungen und Beschränkungen
* unter der Lizenz sind dem Lizenztext zu entnehmen.
*/
import { registerLocaleData } from '@angular/common';
import localeDe from '@angular/common/locales/de';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { MatIcon } from '@angular/material/icon';
import { MatIconTestingModule } from '@angular/material/icon/testing';
import { BescheidStatusTextComponent } from '@ods/system';
import { createBescheid } from 'libs/bescheid-shared/src/test/bescheid';
import { FormatFullDatePipe } from 'libs/tech-shared/src/lib/pipe/format-full-date.pipe';
import { OzgcloudSvgIconComponent } from 'libs/ui/src/lib/ui/ozgcloud-svgicon/ozgcloud-svgicon.component';
import { MockComponent } from 'ng-mocks';
import { VorgangDetailBescheidenResultStatusComponent } from './vorgang-detail-bescheiden-result-status.component';
registerLocaleData(localeDe);
describe('VorgangDetailBescheidenResultStatusComponent', () => {
let component: VorgangDetailBescheidenResultStatusComponent;
let fixture: ComponentFixture<VorgangDetailBescheidenResultStatusComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [MatIconTestingModule],
declarations: [
VorgangDetailBescheidenResultStatusComponent,
MatIcon,
FormatFullDatePipe,
MockComponent(OzgcloudSvgIconComponent),
MockComponent(BescheidStatusTextComponent),
],
}).compileComponents();
fixture = TestBed.createComponent(VorgangDetailBescheidenResultStatusComponent);
component = fixture.componentInstance;
component.bescheid = createBescheid();
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
/*
* Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den
* Ministerpräsidenten des Landes Schleswig-Holstein
* Staatskanzlei
* Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
*
* Lizenziert unter der EUPL, Version 1.2 oder - sobald
* diese von der Europäischen Kommission genehmigt wurden -
* Folgeversionen der EUPL ("Lizenz");
* Sie dürfen dieses Werk ausschließlich gemäß
* dieser Lizenz nutzen.
* Eine Kopie der Lizenz finden Sie hier:
*
* https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
*
* Sofern nicht durch anwendbare Rechtsvorschriften
* gefordert oder in schriftlicher Form vereinbart, wird
* die unter der Lizenz verbreitete Software "so wie sie
* ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
* ausdrücklich oder stillschweigend - verbreitet.
* Die sprachspezifischen Genehmigungen und Beschränkungen
* unter der Lizenz sind dem Lizenztext zu entnehmen.
*/
import { Bescheid } from '@alfa-client/bescheid-shared';
import { Component, Input } from '@angular/core';
@Component({
selector: 'alfa-vorgang-detail-bescheiden-result-status',
templateUrl: './vorgang-detail-bescheiden-result-status.component.html',
})
export class VorgangDetailBescheidenResultStatusComponent {
@Input() bescheid: Bescheid;
}
<!--
Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den
Ministerpräsidenten des Landes Schleswig-Holstein
Staatskanzlei
Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
Lizenziert unter der EUPL, Version 1.2 oder - sobald
diese von der Europäischen Kommission genehmigt wurden -
Folgeversionen der EUPL ("Lizenz");
Sie dürfen dieses Werk ausschließlich gemäß
dieser Lizenz nutzen.
Eine Kopie der Lizenz finden Sie hier:
https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
Sofern nicht durch anwendbare Rechtsvorschriften
gefordert oder in schriftlicher Form vereinbart, wird
die unter der Lizenz verbreitete Software "so wie sie
ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
ausdrücklich oder stillschweigend - verbreitet.
Die sprachspezifischen Genehmigungen und Beschränkungen
unter der Lizenz sind dem Lizenztext zu entnehmen.
-->
<section
class="flex w-full flex-col justify-between overflow-auto rounded-xl bg-background-100 px-4 py-5"
*ngIf="{
activeStep: activeStep$ | async,
sendByManual: sendByManual$ | async
} as wizardData"
tabindex="0"
>
<div class="flex-1">
<h3
*ngIf="wizardData.sendByManual || wizardData.activeStep !== 3"
class="mb-4 text-base font-bold text-primary-600"
>
Bescheid
</h3>
<div
class="my-4"
data-test-id="bescheid-status-text"
*ngIf="
wizardData.activeStep === 1 ||
wizardData.activeStep === 2 ||
(wizardData.activeStep === 3 && wizardData.sendByManual)
"
>
<alfa-vorgang-detail-bescheiden-result-status
data-test-id="bescheid-status"
[bescheid]="bescheid$ | async"
></alfa-vorgang-detail-bescheiden-result-status>
</div>
<div *ngIf="!wizardData.sendByManual && wizardData.activeStep === 3 && (canSend$ | async)">
<alfa-vorgang-detail-bescheiden-result-nachricht
[bescheidDocumentStateResource]="bescheidDocument$ | async"
[sendWithNachrichtCommandStateResource]="sendWithNachricht$ | async"
data-test-id="bescheid-nachricht-an-antragsteller"
></alfa-vorgang-detail-bescheiden-result-nachricht>
</div>
<div
class="my-4"
*ngIf="
wizardData.activeStep === 2 ||
(wizardData.activeStep === 3 && (wizardData.sendByManual || (canSend$ | async)))
"
data-test-id="bescheid-status-dokument"
>
<alfa-vorgang-detail-bescheiden-result-dokument
data-test-id="bescheid-document"
[deletable]="wizardData.activeStep === 2"
[bescheidDraftStateResource]="bescheidDraftStateResource$ | async"
[uploadBescheidDocumentInProgress]="uploadBescheidDocumentInProgress$ | async"
[createBescheidDocumentInProgress]="createBescheidDocumentInProgress$ | async"
[bescheidDocumentFile]="bescheidDocumentFile$ | async"
[showMissingBescheidDocumentError]="showMissingBescheidDocumentError$ | async"
(deleteFile)="deleteBescheidDocument()"
></alfa-vorgang-detail-bescheiden-result-dokument>
</div>
<div
class="my-4"
data-test-id="bescheid-status-attachments"
*ngIf="
wizardData.activeStep === 2 ||
(wizardData.activeStep === 3 && (wizardData.sendByManual || (canSend$ | async)))
"
>
<alfa-vorgang-detail-bescheiden-result-attachments
[deletable]="wizardData.activeStep === 2"
data-test-id="bescheid-attachments"
></alfa-vorgang-detail-bescheiden-result-attachments>
</div>
</div>
<div class="flex-none">
<div class="flex flex-col">
<ng-container *ngIf="wizardData.sendByManual && wizardData.activeStep === 3">
<p class="mb-8 text-base font-normal text-text">
Der Bescheid muss manuell versendet werden.
</p>
<ng-container *ngIf="bescheidDraftStateResource$ | async as bescheidDraftStateResource">
<ods-button-with-spinner
class="self-end"
*ngIf="canSave$ | async"
data-test-id="confirm-and-save-button"
[stateResource]="saveAndSendInProgress$ | async"
text="Antrag bescheiden und speichern"
(clickEmitter)="saveAndSendManually(bescheidDraftStateResource.resource)"
></ods-button-with-spinner>
</ng-container>
<!-- -->
</ng-container>
<ng-container
*ngIf="wizardData.activeStep === 3 && !wizardData.sendByManual && (canSend$ | async)"
>
<ng-container *ngIf="bescheidDraftStateResource$ | async as bescheidDraftStateResource">
<ods-button-with-spinner
class="self-end"
*ngIf="canSend$ | async"
data-test-id="send-button"
[stateResource]="saveAndSendInProgress$ | async"
text="Bescheid senden"
(clickEmitter)="saveAndSendWithNachricht(bescheidDraftStateResource.resource)"
></ods-button-with-spinner>
</ng-container>
</ng-container>
</div>
</div>
</section>
/*
* Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den
* Ministerpräsidenten des Landes Schleswig-Holstein
* Staatskanzlei
* Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
*
* Lizenziert unter der EUPL, Version 1.2 oder - sobald
* diese von der Europäischen Kommission genehmigt wurden -
* Folgeversionen der EUPL ("Lizenz");
* Sie dürfen dieses Werk ausschließlich gemäß
* dieser Lizenz nutzen.
* Eine Kopie der Lizenz finden Sie hier:
*
* https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
*
* Sofern nicht durch anwendbare Rechtsvorschriften
* gefordert oder in schriftlicher Form vereinbart, wird
* die unter der Lizenz verbreitete Software "so wie sie
* ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
* ausdrücklich oder stillschweigend - verbreitet.
* Die sprachspezifischen Genehmigungen und Beschränkungen
* unter der Lizenz sind dem Lizenztext zu entnehmen.
*/
import {
Bescheid,
BescheidLinkRel,
BescheidResource,
BescheidService,
DocumentResource,
UploadFileInProgress,
} from '@alfa-client/bescheid-shared';
import { BinaryFileResource } from '@alfa-client/binary-file-shared';
import {
CommandOrder,
CommandResource,
switchMapCommandSuccessfullyDone,
tapOnCommandSuccessfullyDone,
} from '@alfa-client/command-shared';
import { createEmptyStateResource, isLoaded, StateResource } from '@alfa-client/tech-shared';
import { Component, EventEmitter, OnInit, Output } from '@angular/core';
import { hasLink } from '@ngxp/rest';
import { BehaviorSubject, filter, map, Observable, of, OperatorFunction, tap } from 'rxjs';
import { BescheidenFormService } from '../bescheiden.formservice';
type sendBescheid = (BescheidResource: BescheidResource) => Observable<StateResource<CommandResource>>;
@Component({
selector: 'alfa-vorgang-detail-bescheiden-result',
templateUrl: './vorgang-detail-bescheiden-result.component.html',
})
export class VorgangDetailBescheidenResultComponent implements OnInit {
static readonly ADD_DOCUMENTS_STEP: number = 2;
static readonly BESCHEID_VERSENDEN_STEP: number = 3;
@Output() closeDialog: EventEmitter<void> = new EventEmitter();
public activeStep$: Observable<number>;
public bescheid$: Observable<Bescheid>;
public sendByManual$: Observable<boolean>;
public bescheidDraftStateResource$: Observable<StateResource<BescheidResource>> =
of(createEmptyStateResource<BescheidResource>());
public bescheidDocumentFile$: Observable<StateResource<BinaryFileResource>> =
of(createEmptyStateResource<BinaryFileResource>());
public bescheidDocument$: Observable<StateResource<DocumentResource>> = of(createEmptyStateResource<DocumentResource>());
public saveAndSendInProgress$: Observable<StateResource<CommandResource>> = of(createEmptyStateResource<CommandResource>());
public uploadBescheidDocumentInProgress$: Observable<UploadFileInProgress> = of({
loading: false,
});
public createBescheidDocumentInProgress$: Observable<StateResource<CommandResource>> =
of(createEmptyStateResource<CommandResource>());
public sendWithNachricht$: BehaviorSubject<StateResource<CommandResource>> = new BehaviorSubject(
createEmptyStateResource<CommandResource>(),
);
public canSave$: Observable<boolean> = of(true);
public canSend$: Observable<boolean> = of(true);
public showMissingBescheidDocumentError$: Observable<boolean> = of(false);
public bescheidLinkRel = BescheidLinkRel;
constructor(
private bescheidService: BescheidService,
public formService: BescheidenFormService,
) {}
ngOnInit(): void {
this.bescheidDraftStateResource$ = this.bescheidService.getBescheidDraft();
this.bescheidDocumentFile$ = this.bescheidService.getBescheidDocumentFile();
this.uploadBescheidDocumentInProgress$ = this.bescheidService.getUploadBescheidDocumentInProgress();
this.createBescheidDocumentInProgress$ = this.bescheidService.getCreateBescheidDocumentInProgress();
this.bescheidDocument$ = this.bescheidService.getBescheidDocument();
this.activeStep$ = this.getActiveStep();
this.bescheid$ = this.formService.getBescheidChanges();
this.sendByManual$ = this.getIsSendManually();
this.canSave$ = this.bescheidDraftStateResource$.pipe(
filter(isLoaded),
map((stateResource: StateResource<BescheidResource>) => hasLink(stateResource.resource, BescheidLinkRel.BESCHEIDEN)),
);
this.canSend$ = this.bescheidDraftStateResource$.pipe(
filter(isLoaded),
map((stateResource: StateResource<BescheidResource>) =>
hasLink(stateResource.resource, BescheidLinkRel.BESCHEIDEN_UND_SENDEN),
),
);
this.showMissingBescheidDocumentError$ = this.formService.getShowMissingBescheidDocumentError();
}
getActiveStep(): Observable<number> {
return this.formService.getActiveStep().pipe(tap((step: number) => this.resetStateOnStepChange(step)));
}
getIsSendManually(): Observable<boolean> {
return this.formService.isSendByManual().pipe(tap(() => this.resetSend()));
}
private resetStateOnStepChange(step: number): void {
if (this.isNotLast(step)) this.resetSend();
if (this.shouldClearAttachmentInProgress(step)) this.bescheidService.clearAttachmentUpload();
}
private isNotLast(step: number): boolean {
return step < VorgangDetailBescheidenResultComponent.BESCHEID_VERSENDEN_STEP;
}
private shouldClearAttachmentInProgress(step: number): boolean {
return step != VorgangDetailBescheidenResultComponent.ADD_DOCUMENTS_STEP;
}
resetSend(): void {
this.saveAndSendInProgress$ = of(createEmptyStateResource<CommandResource>());
this.sendWithNachricht$.next(createEmptyStateResource<CommandResource>());
}
public deleteBescheidDocument(): void {
this.formService.clearBescheidDocumentFile();
this.bescheidService.deleteBescheidDocument();
}
public saveAndSendManually(bescheidDraft: BescheidResource): void {
this.formService.clearNachricht();
this.saveAndSendInProgress$ = this.doUpdateAndSend(bescheidDraft, (bescheidResource) =>
this.bescheidService.sendBescheidManually(bescheidResource),
);
}
public saveAndSendWithNachricht(bescheidDraft: BescheidResource): void {
this.saveAndSendInProgress$ = this.doUpdateAndSend(bescheidDraft, (bescheidResource) =>
this.bescheidService
.sendBescheidToAntragsteller(bescheidResource)
.pipe(
tap((sendCommandStateResource: StateResource<CommandResource>) =>
this.sendWithNachricht$.next(sendCommandStateResource),
),
),
);
}
doUpdateAndSend(bescheidDraft: BescheidResource, send: sendBescheid): Observable<StateResource<CommandResource>> {
return this.formService.submit(this.doSendAfterSuccessfullyUpdate(bescheidDraft, send)).pipe(
tapOnCommandSuccessfullyDone((commandStateResource: StateResource<CommandResource>) => {
if (this.isSendBescheidCommand(commandStateResource)) {
this.closeDialog.emit();
}
}),
);
}
doSendAfterSuccessfullyUpdate(
bescheidDraft: BescheidResource,
send: sendBescheid,
): OperatorFunction<StateResource<CommandResource>, StateResource<CommandResource>> {
return switchMapCommandSuccessfullyDone(() => send(bescheidDraft));
}
private isSendBescheidCommand(commandStateResource: StateResource<CommandResource>): boolean {
return commandStateResource.resource.order == CommandOrder.SEND_BESCHEID;
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment