Skip to content

Commit

Permalink
feat: HASSUYP-409 Järjestettävä taulukko hyväksymisesityksen suunnite…
Browse files Browse the repository at this point in the history
…lma-aineistolle (#1246)

* toteuta eka vedos taulukosta

* yksinkertaista taulukon renderöintiä lisäämällä lomakkeella olevia tietoja

* päivitä ohjetekstiä kategorisoimattomissa

* korjaa suunnitelma-aineistot esikatselussa lisäämällä niihin kategoriatieto

* lisää validointi sille, että aineistot on kategorisoituna, jotta hyväksymisesitys voidaan viedä hyväksyttäväksi

* mahdollista tallennaJaLahetaHyvaksyttavaksi vaikkei esitystä olisi aiemmin tallennettu kertaakaan

* lisää aineistomäärätiedot näkyviin ja korjaa oletuksena näytettävät '-'

* Korjaa aikaleimatarkistukset jos aika on sama

* korjaa aineistoKategorioiden hakeminen

* piilota hyväksymisesityksen muokkauslomakkeilta deprecoidut kategoriat

---------

Co-authored-by: tomi korkalainen <tomi.korkalainen@cgi.com>
Co-authored-by: Petri Hakala <petri.hakala@cgi.com>
  • Loading branch information
3 people authored Jul 31, 2024
1 parent 6818019 commit c3cc877
Show file tree
Hide file tree
Showing 24 changed files with 570 additions and 139 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { TestType } from "hassu-common/schema/common";
import { SqsClient } from "../aineistoHandling/sqsClient";
import { HyvaksymisEsitysAineistoOperation } from "../aineistoHandling/sqsEvent";
import dayjs from "dayjs";
import { getAineistoKategoriat, kategorisoimattomatId } from "hassu-common/aineistoKategoriat";

/**
* Hakee halutun projektin tiedot ja tallentaa inputin perusteella muokattavalle hyväksymisesitykselle uudet tiedot
Expand All @@ -43,7 +44,7 @@ export default async function tallennaHyvaksymisEsitysJaLahetaHyvaksyttavaksi(in
// Adaptoi muokattava hyvaksymisesitys
const newMuokattavaHyvaksymisEsitys = adaptHyvaksymisEsitysToSave(projektiInDB.muokattavaHyvaksymisEsitys, muokattavaHyvaksymisEsitys);
// Validoi, että hyväksyttäväksi lähetettävällä hyväksymisEsityksellä on kaikki kentät kunnossa
validateUpcoming(newMuokattavaHyvaksymisEsitys, projektiInDB.aineistoHandledAt);
validateUpcoming(newMuokattavaHyvaksymisEsitys, projektiInDB.aineistoHandledAt, projektiInDB.velho?.tyyppi);
// Persistoi uudet tiedostot
const uudetTiedostot = getHyvaksymisEsityksenUudetLadatutTiedostot(projektiInDB.muokattavaHyvaksymisEsitys, muokattavaHyvaksymisEsitys);
if (uudetTiedostot.length) {
Expand Down Expand Up @@ -95,7 +96,7 @@ async function validateCurrent(projektiInDB: HyvaksymisEsityksenTiedot, input: A
// Toiminnon tekijän on oltava projektihenkilö
requirePermissionMuokkaa(projektiInDB);
// Projektilla on oltava muokkaustilainen hyväksymisesitys
if (projektiInDB.muokattavaHyvaksymisEsitys?.tila !== API.HyvaksymisTila.MUOKKAUS) {
if (projektiInDB.muokattavaHyvaksymisEsitys?.tila && projektiInDB.muokattavaHyvaksymisEsitys?.tila !== API.HyvaksymisTila.MUOKKAUS) {
throw new IllegalArgumentError("Projektilla ei ole muokkaustilaista hyväksymisesitystä");
}
if (input.versio !== projektiInDB.versio) {
Expand All @@ -109,13 +110,29 @@ async function validateCurrent(projektiInDB: HyvaksymisEsityksenTiedot, input: A
await validateVaiheOnAktiivinen(projektiInDB);
}

function validateUpcoming(muokattavaHyvaksymisEsitys: MuokattavaHyvaksymisEsitys, aineistotHandledAt: string | undefined | null) {
function validateUpcoming(
muokattavaHyvaksymisEsitys: MuokattavaHyvaksymisEsitys,
aineistotHandledAt: string | undefined | null,
projektiTyyppi: API.ProjektiTyyppi | null | undefined
) {
// Aineistojen ja ladattujen tiedostojen on oltava valmiita
const aineistot = getHyvaksymisEsityksenAineistot(muokattavaHyvaksymisEsitys);
const handledAt = aineistotHandledAt ? dayjs(aineistotHandledAt) : null;
if (
aineistot.length > 0 &&
(!aineistotHandledAt || !aineistot.every((aineisto) => dayjs(aineistotHandledAt).isAfter(dayjs(aineisto.lisatty))))
(!handledAt || !aineistot.every((aineisto) => {
const lisattyDate = aineisto.lisatty ? dayjs(aineisto.lisatty) : null;
return handledAt.isAfter(lisattyDate) || handledAt.isSame(lisattyDate);
}))
) {
throw new IllegalArgumentError("Aineistojen on oltava valmiita ennen kuin hyväksymisesitys lähetetään hyväksyttäväksi.");
}
const kategoriaIds = getAineistoKategoriat({ projektiTyyppi, hideDeprecated: true }).listKategoriaIds();
if (
muokattavaHyvaksymisEsitys.suunnitelma?.some(
(aineisto) => !aineisto.kategoriaId || aineisto.kategoriaId === kategorisoimattomatId || !kategoriaIds.includes(aineisto.kategoriaId)
)
) {
throw new IllegalArgumentError("Suunnitelma-aineistojen on oltava kategorisoituna, jotta hyväksymisesitys voidaan hyväksyä.");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,11 @@ export function adaptAineistotToAPI({
path: string;
}): API.AineistoNew[] | undefined {
if (aineistot && aineistot.length > 0) {
const handledAt = aineistotHandledAt ? dayjs(aineistotHandledAt) : null;
return [...aineistot].map((aineisto) => {
const { dokumenttiOid, kategoriaId, nimi, lisatty, uuid } = aineisto;
const tuotu = !!(aineistotHandledAt && dayjs(aineistotHandledAt).isAfter(dayjs(lisatty)));
const lisattyDate = lisatty ? dayjs(lisatty) : null;
const tuotu = !!(handledAt && (handledAt.isAfter(lisattyDate) || handledAt.isSame(lisattyDate)));
const apiAineisto: API.AineistoNew = {
__typename: "AineistoNew",
dokumenttiOid,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export type FileInfo = {
valmis: boolean;
tuotu?: string | undefined | null;
kunta?: number | null;
kategoriaId?: string | null;
};

type ProjektinAineistot = {
Expand All @@ -40,7 +41,9 @@ type ProjektinAineistot = {
};

function aineistoNewIsReady(lisatty: string, aineistoHandledAt?: string | null): boolean {
return !!(aineistoHandledAt && dayjs(aineistoHandledAt).isAfter(dayjs(lisatty)));
const handledAt = aineistoHandledAt ? dayjs(aineistoHandledAt) : null;
const lisattyDate = lisatty ? dayjs(lisatty) : null;
return !!(handledAt && (handledAt.isAfter(lisattyDate) || handledAt.isSame(lisattyDate)));
}

/**
Expand Down Expand Up @@ -93,13 +96,14 @@ export default function collectHyvaksymisEsitysAineistot(
valmis: aineistoNewIsReady(tiedosto.lisatty, aineistoHandledAt),
}));
const muutAineistot: FileInfo[] = muuAineistoOmaltaKoneelta.concat(muuAineistoVelhosta);
const suunnitelma = (hyvaksymisEsitys?.suunnitelma ?? []).map((aineisto) => {
const suunnitelma = (hyvaksymisEsitys?.suunnitelma ?? []).map<FileInfo>((aineisto) => {
const kategoriaFolder = getZipFolder(aineisto.kategoriaId, projekti.velho?.tyyppi) ?? "Kategorisoimattomat";
return {
s3Key: joinPath(path, "suunnitelma", adaptFileName(aineisto.nimi)),
zipFolder: joinPath("Suunnitelma", kategoriaFolder),
nimi: aineisto.nimi,
tuotu: aineisto.lisatty,
kategoriaId: aineisto.kategoriaId,
valmis: aineistoNewIsReady(aineisto.lisatty, aineistoHandledAt),
};
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export async function adaptFileInfoToLadattavaTiedosto(fileInfo: FileInfo): Prom
} else {
linkki = "";
}
return { __typename: "LadattavaTiedosto", nimi: fileInfo.nimi, linkki, tuotu: fileInfo.tuotu };
return { __typename: "LadattavaTiedosto", nimi: fileInfo.nimi, linkki, kategoriaId: fileInfo.kategoriaId, tuotu: fileInfo.tuotu };
}
export async function adaptFileInfoToKunnallinenLadattavaTiedosto(fileInfo: FileInfo): Promise<API.KunnallinenLadattavaTiedosto> {
let linkki;
Expand Down
45 changes: 29 additions & 16 deletions common/schema/hyvaksymisEsitysSchema.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
import { ValidationModeState } from "../ProjektiValidationContext";
import { ProjektiLisatiedolla, ValidationModeState } from "../ProjektiValidationContext";
import * as Yup from "yup";
import {
TestType,
getAineistotNewSchema,
getLadatutTiedostotNewSchema,
isTestTypeBackend,
isTestTypeFrontend,
isValidationModePublish,
} from "./common";
import { TestType, getAineistotNewSchema, getLadatutTiedostotNewSchema, isTestTypeBackend, isValidationModePublish } from "./common";
import { notInPastCheck, paivamaara } from "./paivamaaraSchema";
import { mapValues } from "lodash";
import { getAineistoKategoriat } from "../aineistoKategoriat";
import { ObjectShape, OptionalObjectSchema, AnyObject, TypeOfShape } from "yup/lib/object";
import { ProjektiTyyppi } from "../graphql/apiModel";

const getKunnallinenLadattuTiedostoSchema = () =>
Yup.object().shape({
Expand Down Expand Up @@ -67,7 +63,6 @@ export const hyvaksymisEsitysSchema = Yup.object().shape({
lausunnot: getLadatutTiedostotNewSchema().defined(),
maanomistajaluettelo: getLadatutTiedostotNewSchema().defined(),
muuAineistoKoneelta: getLadatutTiedostotNewSchema().defined(),
suunnitelma: getAineistotNewSchema(true).defined(),
muuAineistoVelhosta: getAineistotNewSchema(false).defined(),
vastaanottajat: Yup.array()
.of(
Expand All @@ -85,16 +80,34 @@ export const hyvaksymisEsitysSchema = Yup.object().shape({
.min(1)
.defined(),
})
.when("$testType", {
is: isTestTypeFrontend,
then: Yup.object().shape({
muistutukset: Yup.lazy((obj) => Yup.object(mapValues(obj, () => getKunnallinenLadatutTiedostotSchema().defined()))),
}),
})
.when(
["$testType", "$projekti"],
(
[testType, projekti]: [testType: TestType, projekti: ProjektiLisatiedolla],
schema: OptionalObjectSchema<ObjectShape, AnyObject, TypeOfShape<ObjectShape>>
) =>
testType === TestType.FRONTEND
? Yup.object().shape({
muistutukset: Yup.lazy((obj) => Yup.object(mapValues(obj, () => getKunnallinenLadatutTiedostotSchema().defined()))),
suunnitelma: suunnitelmaFrontendSchema(projekti.velho.tyyppi),
})
: schema
)
.when("$testType", {
is: isTestTypeBackend,
then: Yup.object().shape({
muistutukset: getKunnallinenLadatutTiedostotSchema().defined(),
suunnitelma: getAineistotNewSchema(true).defined(),
}),
}),
});

function suunnitelmaFrontendSchema(projektiTyyppi: ProjektiTyyppi | null | undefined) {
const kategorioittenSchema = getAineistoKategoriat({ projektiTyyppi, showKategorisoimattomat: true })
.listKategoriaIds()
.reduce<ObjectShape>((obj, id) => {
obj[id] = getAineistotNewSchema(true).defined();
return obj;
}, {});
return Yup.object().shape(kategorioittenSchema);
}
30 changes: 16 additions & 14 deletions src/components/HyvaksymisEsitys/AineistoPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ export default function HyvaksymisEsitysAineistoPage(props: HyvaksymisEsityksenA
? t(`viranomainen.${projektipaallikonYhteystiedot.elyOrganisaatio}`)
: projektipaallikonYhteystiedot?.organisaatio;

const muistutusMaara = useMemo(() => Object.values(muistutukset).flat().length ?? 0, [muistutukset]);

const kategoriat = useMemo(
() => getAineistoKategoriat({ projektiTyyppi: perustiedot.projektiTyyppi }).listKategoriat(),
[perustiedot.projektiTyyppi]
Expand Down Expand Up @@ -91,31 +93,31 @@ export default function HyvaksymisEsitysAineistoPage(props: HyvaksymisEsityksenA
<HassuGrid cols={3} sx={{ width: { lg: "70%", sm: "100%" }, rowGap: 0, marginTop: "2em", marginBottom: "2.5em" }}>
<HassuGridItem colSpan={1}>
<H4>Suunnitelman nimi</H4>
<p>{suunnitelmanNimi ?? "-"}</p>
<p>{suunnitelmanNimi || "-"}</p>
</HassuGridItem>
<HassuGridItem colSpan={2}>
<H4>Asiatunnus</H4>
<p>{asiatunnus ?? "-"}</p>
<p>{asiatunnus || "-"}</p>
</HassuGridItem>
<HassuGridItem colSpan={1}>
<H4>Vastuuorganisaatio</H4>
<p>{vastuuorganisaatio ? t(`viranomainen.${vastuuorganisaatio}`) : "-"}</p>
</HassuGridItem>
<HassuGridItem colSpan={2}>
<H4>Y-tunnus</H4>
<p>{yTunnus ?? "-"}</p>
<p>{yTunnus || "-"}</p>
</HassuGridItem>
<HassuGridItem colSpan={1}>
<H4>OVT-tunnus</H4>
<p>{laskutustiedot?.ovtTunnus ?? "-"}</p>
<p>{laskutustiedot?.ovtTunnus || "-"}</p>
</HassuGridItem>
<HassuGridItem colSpan={2}>
<H4>Verkkolaskuoperaattorin välittäjätunnus</H4>
<p>{laskutustiedot?.verkkolaskuoperaattorinTunnus ?? "-"}</p>
<p>{laskutustiedot?.verkkolaskuoperaattorinTunnus || "-"}</p>
</HassuGridItem>
<HassuGridItem colSpan={3}>
<H4>Viitetieto</H4>
<p>{laskutustiedot?.viitetieto ?? "-"}</p>
<p>{laskutustiedot?.viitetieto || "-"}</p>
</HassuGridItem>
</HassuGrid>
</SectionContent>
Expand All @@ -136,7 +138,7 @@ export default function HyvaksymisEsitysAineistoPage(props: HyvaksymisEsityksenA

<Section>
<H2>Hyväksymisesityksen aineisto</H2>
<H3>Hyväksymisesitys</H3>
<H3>{`Hyväksymisesitys (${hyvaksymisEsitys?.length ?? 0})`}</H3>
{hyvaksymisEsitys?.length ? (
<ul style={{ listStyle: "none" }}>
{hyvaksymisEsitys?.map((tiedosto, index) => (
Expand All @@ -148,7 +150,7 @@ export default function HyvaksymisEsitysAineistoPage(props: HyvaksymisEsityksenA
) : (
<div>Ei aineistoja</div>
)}
<H3>Suunnitelma</H3>
<H3>{`Suunnitelma (${suunnitelma?.length ?? 0})`}</H3>
<SuunnittelmaLadattavatTiedostotAccordion kategoriat={kategoriat} aineistot={suunnitelma} esikatselu={!!props.esikatselu} />
</Section>
<Section>
Expand All @@ -157,12 +159,12 @@ export default function HyvaksymisEsitysAineistoPage(props: HyvaksymisEsityksenA
items={[
{
id: "1",
title: <H3 sx={{ margin: 0 }}>Muistutukset</H3>,
title: <H3 sx={{ margin: 0 }}>{`Muistutukset (${muistutusMaara})`}</H3>,
content: (
<div>
{Object.keys(muistutukset).map((kunta) => (
<div key={kunta} style={{ marginTop: "1em" }}>
<H5>{kuntametadata.nameForKuntaId(parseInt(kunta), "fi")}</H5>
<H5>{kuntametadata.nameForKuntaId(parseInt(kunta), "fi") + ` (${muistutukset[kunta]?.length ?? 0})`}</H5>
{muistutukset[kunta]?.length ? (
<ul style={{ listStyle: "none" }}>
{muistutukset[kunta]?.map((tiedosto, index) => (
Expand All @@ -181,7 +183,7 @@ export default function HyvaksymisEsitysAineistoPage(props: HyvaksymisEsityksenA
},
{
id: "2",
title: <H3 sx={{ margin: 0 }}>Lausunnot</H3>,
title: <H3 sx={{ margin: 0 }}>{`Lausunnot (${lausunnot?.length ?? 0})`}</H3>,
content: lausunnot?.length ? (
<ul style={{ listStyle: "none" }}>
{lausunnot?.map((tiedosto, index) => (
Expand All @@ -196,7 +198,7 @@ export default function HyvaksymisEsitysAineistoPage(props: HyvaksymisEsityksenA
},
{
id: "3",
title: <H3 sx={{ margin: 0 }}>Maanomistajaluettelo</H3>,
title: <H3 sx={{ margin: 0 }}>{`Maanomistajaluettelo (${maanomistajaluettelo?.length ?? 0})`}</H3>,
content: maanomistajaluettelo?.length ? (
<ul style={{ listStyle: "none" }}>
{maanomistajaluettelo?.map((tiedosto, index) => (
Expand All @@ -211,7 +213,7 @@ export default function HyvaksymisEsitysAineistoPage(props: HyvaksymisEsityksenA
},
{
id: "4",
title: <H3 sx={{ margin: 0 }}>Kuulutukset ja kutsu vuorovaikutukseen</H3>,
title: <H3 sx={{ margin: 0 }}>{`Kuulutukset ja kutsu vuorovaikutukseen (${kuulutuksetJaKutsu?.length ?? 0})`}</H3>,
content: kuulutuksetJaKutsu?.length ? (
<ul style={{ listStyle: "none" }}>
{kuulutuksetJaKutsu?.map((tiedosto, index) => (
Expand All @@ -232,7 +234,7 @@ export default function HyvaksymisEsitysAineistoPage(props: HyvaksymisEsityksenA
items={[
{
id: "3",
title: <H2 sx={{ margin: 0 }}>Muu tekninen aineisto</H2>,
title: <H2 sx={{ margin: 0 }}>{`Muu tekninen aineisto (${muutAineistot?.length ?? 0})`}</H2>,
content: muutAineistot?.length ? (
<ul style={{ listStyle: "none" }}>
{muutAineistot?.map((tiedosto, index) => (
Expand Down
15 changes: 13 additions & 2 deletions src/components/HyvaksymisEsitys/HyvaksymisEsitysLomake.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import Suunnitelma from "./LomakeComponents/Suunnitelma";
import MuokkausLomakePainikkeet from "./LomakeComponents/MuokkausLomakePainikkeet";
import useValidationMode from "src/hooks/useValidationMode";
import { TestType } from "common/schema/common";
import { getAineistoKategoriat } from "common/aineistoKategoriat";

type Props = {
hyvaksymisEsityksenTiedot: HyvaksymisEsityksenTiedot;
Expand Down Expand Up @@ -55,6 +56,16 @@ export default function HyvaksymisEsitysLomake({ hyvaksymisEsityksenTiedot }: Re
useFormReturn.reset(defaultValues);
}, [useFormReturn, defaultValues]);

const aineistoKategoriat = useMemo(
() =>
getAineistoKategoriat({
projektiTyyppi: hyvaksymisEsityksenTiedot.perustiedot.projektiTyyppi,
showKategorisoimattomat: true,
hideDeprecated: true,
}),
[hyvaksymisEsityksenTiedot.perustiedot.projektiTyyppi]
);

if (!hyvaksymisEsityksenTiedot) {
return null;
}
Expand Down Expand Up @@ -122,7 +133,7 @@ export default function HyvaksymisEsitysLomake({ hyvaksymisEsityksenTiedot }: Re
<H3 variant="h2">Hyväksymisesitykseen liitettävä aineisto</H3>
<LinkkiHyvEsAineistoon hash={hyvaksymisEsityksenTiedot.hyvaksymisEsitys?.hash} oid={hyvaksymisEsityksenTiedot.oid} />
<HyvaksymisEsitysTiedosto tiedostot={hyvaksymisEsityksenTiedot.hyvaksymisEsitys?.hyvaksymisEsitys} />
<Suunnitelma />
<Suunnitelma aineistoKategoriat={aineistoKategoriat} />
<H4 variant="h3">Vuorovaikutus</H4>
<p>Tuo omalta koneelta suunnitelmalle annetut muistutukset, lausunnot ja maanomistajaluettelo.</p>
<Muistutukset
Expand Down Expand Up @@ -151,7 +162,7 @@ export default function HyvaksymisEsitysLomake({ hyvaksymisEsityksenTiedot }: Re
<Section>
<AineistonEsikatselu />
</Section>
<MuokkausLomakePainikkeet hyvaksymisesitys={hyvaksymisEsityksenTiedot} />
<MuokkausLomakePainikkeet aineistoKategoriat={aineistoKategoriat} hyvaksymisesitys={hyvaksymisEsityksenTiedot} />
</form>
</FormProvider>
)}
Expand Down
12 changes: 6 additions & 6 deletions src/components/HyvaksymisEsitys/HyvaksymisEsitysLukutila.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -141,31 +141,31 @@ export default function HyvaksymisEsitysLukutila({
<HassuGrid cols={3} sx={{ width: { lg: "70%", sm: "100%" }, rowGap: 0, marginTop: "2em", marginBottom: "2.5em" }}>
<HassuGridItem colSpan={1}>
<H5>Suunnitelman nimi</H5>
<p>{perustiedot.suunnitelmanNimi ?? "-"}</p>
<p>{perustiedot.suunnitelmanNimi || "-"}</p>
</HassuGridItem>
<HassuGridItem colSpan={2}>
<H5>Asiatunnus</H5>
<p>{perustiedot.asiatunnus ?? "-"}</p>
<p>{perustiedot.asiatunnus || "-"}</p>
</HassuGridItem>
<HassuGridItem colSpan={1}>
<H5>Vastuuorganisaatio</H5>
<p>{perustiedot.vastuuorganisaatio ? t(`viranomainen.${perustiedot.vastuuorganisaatio}`) : "-"}</p>
</HassuGridItem>
<HassuGridItem colSpan={2}>
<H5>Y-tunnus</H5>
<p>{perustiedot.yTunnus ?? "-"}</p>
<p>{perustiedot.yTunnus || "-"}</p>
</HassuGridItem>
<HassuGridItem colSpan={1}>
<H5>OVT-tunnus</H5>
<p>{laskutustiedot?.ovtTunnus ?? "-"}</p>
<p>{laskutustiedot?.ovtTunnus || "-"}</p>
</HassuGridItem>
<HassuGridItem colSpan={2}>
<H5>Verkkolaskuoperaattorin välittäjätunnus</H5>
<p>{laskutustiedot?.verkkolaskuoperaattorinTunnus ?? "-"}</p>
<p>{laskutustiedot?.verkkolaskuoperaattorinTunnus || "-"}</p>
</HassuGridItem>
<HassuGridItem colSpan={3}>
<H5>Viitetieto</H5>
<p>{laskutustiedot?.viitetieto ?? "-"}</p>
<p>{laskutustiedot?.viitetieto || "-"}</p>
</HassuGridItem>
</HassuGrid>
</Section>
Expand Down
Loading

0 comments on commit c3cc877

Please sign in to comment.