Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Various SAP reporting fixes #733

Merged
merged 1 commit into from
Oct 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 22 additions & 3 deletions backend/src/components/code/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { sql } from 'slonik';

import { getPool } from '@backend/db';

import { Code, CodeId, codeSchema } from '@shared/schema/code';
import { Code, CodeId, EXPLICIT_EMPTY, codeSchema } from '@shared/schema/code';

const codeSelectFragment = sql.fragment`
SELECT
Expand All @@ -17,11 +17,30 @@ const codeSelectFragment = sql.fragment`
FROM app.code
`;

export async function getCodesForCodeList(codeListId: Code['id']['codeListId']) {
return getPool().any(sql.type(codeSchema)`
export async function getCodesForCodeList(
codeListId: Code['id']['codeListId'],
emptySelection: boolean = false
) {
const results = await getPool().any(sql.type(codeSchema)`
${codeSelectFragment}
WHERE (code.id).code_list_id = ${codeListId}
`);

return emptySelection
? [
{
id: {
id: EXPLICIT_EMPTY,
codeListId,
},
text: {
fi: 'Tyhjä arvo',
en: 'Empty value',
},
},
...results,
]
: results;
}

export function codeIdFragment(
Expand Down
43 changes: 29 additions & 14 deletions backend/src/components/sap/environmentCodeReport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,33 @@ import { z } from 'zod';

import { getPool, sql, textToTsQuery } from '@backend/db';

import { EXPLICIT_EMPTY } from '@shared/schema/code';
import { EnvironmentCodeReportQuery, environmentCodeReportSchema } from '@shared/schema/sapReport';

function filterPlantFragment(plants?: EnvironmentCodeReportQuery['filters']['plants']) {
if (!plants || plants.length === 0) return sql.fragment`true`;

const includeEmpty = plants.includes(EXPLICIT_EMPTY);
const inArrayFragment = sql.fragment`plant = ANY(${sql.array(plants, 'text')})`;
return includeEmpty ? sql.fragment`(${inArrayFragment} OR plant IS NULL)` : inArrayFragment;
}

function filterReasonForEnvironmentalInvestmentFragment(
reasonsForEnvironmentalInvestment?: EnvironmentCodeReportQuery['filters']['reasonsForEnvironmentalInvestment']
) {
if (!reasonsForEnvironmentalInvestment || reasonsForEnvironmentalInvestment.length === 0)
return sql.fragment`true`;

const includeEmpty = reasonsForEnvironmentalInvestment.includes(EXPLICIT_EMPTY);
const inArrayFragment = sql.fragment`"reasonForEnvironmentalInvestment" = ANY(${sql.array(
reasonsForEnvironmentalInvestment,
'text'
)})`;
return includeEmpty
? sql.fragment`(${inArrayFragment} OR "reasonForEnvironmentalInvestment" IS NULL)`
: inArrayFragment;
}

function environmentCodeReportFragment(params?: Partial<EnvironmentCodeReportQuery>) {
const years = params?.filters?.years ?? [];
return sql.fragment`
Expand Down Expand Up @@ -60,20 +85,10 @@ function environmentCodeReportFragment(params?: Partial<EnvironmentCodeReportQue
)`
: sql.fragment`true`
}
AND ${
params?.filters?.plants && params.filters.plants.length > 0
? sql.fragment`plant = ANY(${sql.array(params.filters.plants, 'text')})`
: sql.fragment`true`
}
AND ${
params?.filters?.reasonsForEnvironmentalInvestment &&
params.filters.reasonsForEnvironmentalInvestment.length > 0
? sql.fragment`"reasonForEnvironmentalInvestment" = ANY(${sql.array(
params.filters.reasonsForEnvironmentalInvestment,
'text'
)})`
: sql.fragment`true`
}
AND (${filterPlantFragment(params?.filters?.plants)})
AND (${filterReasonForEnvironmentalInvestmentFragment(
params?.filters?.reasonsForEnvironmentalInvestment
)})
${
params?.sort
? sql.fragment`ORDER BY ${sql.identifier([params.sort.key])} ${
Expand Down
2 changes: 1 addition & 1 deletion backend/src/router/code.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@ import { TRPC } from '.';
export const createCodeRouter = (t: TRPC) =>
t.router({
get: t.procedure.input(codeSearchSchema).query(async ({ input }) => {
return getCodesForCodeList(input.codeListId);
return getCodesForCodeList(input.codeListId, input.allowEmptySelection);
}),
});
4 changes: 3 additions & 1 deletion backend/src/router/sapReport.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { EXPLICIT_EMPTY } from 'tre-hanna-shared/src/schema/code';
import { z } from 'zod';

import {
Expand Down Expand Up @@ -83,7 +84,8 @@ export const createSapReportRouter = (t: TRPC) =>
WHERE plant IS NOT NULL
ORDER BY plant ASC
`);
return rows.map((row) => row.plant);
const plants = rows.map((row) => row.plant);
return [EXPLICIT_EMPTY, ...plants];
}),

getYears: t.procedure.query(async () => {
Expand Down
17 changes: 14 additions & 3 deletions frontend/src/components/forms/CodeSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { useMemo } from 'react';
import { trpc } from '@frontend/client';
import { langAtom } from '@frontend/stores/lang';

import type { Code, CodeId } from '@shared/schema/code';
import { type Code, type CodeId, EXPLICIT_EMPTY } from '@shared/schema/code';

import { MultiSelect } from './MultiSelect';

Expand All @@ -15,6 +15,7 @@ type Props = {
onBlur?: () => void;
getLabel?: (code: Code) => string;
showIdInLabel?: boolean;
allowEmptySelection?: boolean;
} & (
| {
multiple: true;
Expand All @@ -40,16 +41,26 @@ export function CodeSelect({
onBlur,
showIdInLabel,
maxTags,
allowEmptySelection,
}: Props) {
const codes = trpc.code.get.useQuery({ codeListId }, { staleTime: 60 * 60 * 1000 });
const codes = trpc.code.get.useQuery(
{ codeListId, allowEmptySelection },
{ staleTime: 60 * 60 * 1000 }
);
const lang = useAtomValue(langAtom);

function getCode(id: string) {
return codes.data?.find((code) => code.id.id === id);
}

function getLabel(code: Code) {
return [showIdInLabel && code.id.id, code.text[lang]].filter(Boolean).join(' ');
// empty selection (00) label is not shown
let labelId = showIdInLabel ? code.id.id : null;
if (code.id.id === EXPLICIT_EMPTY) {
labelId = null;
}

return [labelId, code.text[lang]].filter(Boolean).join(' ');
}

const selection = useMemo(() => {
Expand Down
6 changes: 3 additions & 3 deletions frontend/src/views/SapReports/BlanketContractReport.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,20 +59,20 @@ export function BlanketContractReport() {
title: tr('sapReports.blanketContracts.totalDebit'),
align: 'right',
format(value) {
return formatCurrency(value ?? null);
return formatCurrency(value ?? 0);
},
},
totalCredit: {
title: tr('sapReports.blanketContracts.totalCredit'),
align: 'right',
format(value) {
return formatCurrency(value ?? null);
return formatCurrency(value ?? 0);
},
},
totalActuals: {
title: tr('sapReports.blanketContracts.totalActuals'),
format(value) {
return formatCurrency(value ?? null);
return formatCurrency(value ?? 0);
},
align: 'right',
},
Expand Down
12 changes: 8 additions & 4 deletions frontend/src/views/SapReports/EnvironmentalCodeReport.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,11 @@ export function EnvironmentalCodeReport() {
title: tr('sapReports.environmentCodes.companyCode'),
align: 'right',
format(value) {
return value && isInternalCompany(value) ? value : tr('sapReports.externalCompany');
if (!value) {
return '';
} else {
return value && isInternalCompany(value) ? value : tr('sapReports.externalCompany');
}
},
},
companyCodeText: {
Expand All @@ -65,21 +69,21 @@ export function EnvironmentalCodeReport() {
title: tr('sapReports.environmentCodes.totalDebit'),
align: 'right',
format(value) {
return formatCurrency(value ?? null);
return formatCurrency(value ?? 0);
},
},
totalCredit: {
title: tr('sapReports.environmentCodes.totalCredit'),
align: 'right',
format(value) {
return formatCurrency(value ?? null);
return formatCurrency(value ?? 0);
},
},
totalActuals: {
title: tr('sapReports.environmentCodes.totalActuals'),
align: 'right',
format(value) {
return formatCurrency(value);
return formatCurrency(value ?? 0);
},
},
}}
Expand Down
12 changes: 10 additions & 2 deletions frontend/src/views/SapReports/EnvironmentalCodeReportFilters.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,16 @@ import {
yearsAtom,
} from '@frontend/stores/sapReport/environmentalCodeReportFilters';

import { EXPLICIT_EMPTY } from '@shared/schema/code';

import { ReportSummary } from './ReportSummary';

export function EnvironmentalCodeReportFilters() {
const tr = useTranslations();
const notify = useNotifications();

const [text, setText] = useAtom(textAtom);
const [plants, setPlants] = useAtom(plantsAtom);
const [plants, setPlants] = useAtom<string[]>(plantsAtom);
const [reasonsForEnvironmentalInvestment, setReasonsForEnvironmentalInvestment] = useAtom(
reasonsForEnvironmentalInvestmentAtom
);
Expand Down Expand Up @@ -94,16 +96,22 @@ export function EnvironmentalCodeReportFilters() {
onChange={setReasonsForEnvironmentalInvestment}
maxTags={1}
showIdInLabel
allowEmptySelection
/>
</FormControl>
<FormControl>
<FormLabel htmlFor="plants">{tr('sapReports.environmentCodes.plant')}</FormLabel>
<MultiSelect
<MultiSelect<string>
id="plants"
options={allPlants ?? []}
// use the plant itself as id
getOptionId={(opt) => opt}
loading={allPlantsLoading}
value={plants ?? []}
onChange={setPlants}
getOptionLabel={(opt) =>
opt === EXPLICIT_EMPTY ? tr('sapReports.explicitEmpty') : opt
}
multiple
maxTags={3}
/>
Expand Down
1 change: 1 addition & 0 deletions shared/src/language/fi.json
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,7 @@
"sapReports.downloadReport": "Lataa raportti",
"sapReports.externalCompany": "Kohdistamaton",
"sapReports.reportFailed": "Raportin luonti epäonnistui.",
"sapReports.explicitEmpty": "Tyhjä arvo",
"workTable.search.budgetTitle": "Talousarvio",
"workTable.search.actualTitle": "Toteuma",
"workTable.startDate": "Hakuaikavälin alku",
Expand Down
5 changes: 5 additions & 0 deletions shared/src/schema/code.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ import { z } from 'zod';

import { Language, languages } from '../language';

export const EXPLICIT_EMPTY = '0';

const emptyValueSchema = z.literal(EXPLICIT_EMPTY);

const codeIdRegex = /^\d{0,4}$/;

export const codeId = z.string().regex(codeIdRegex);
Expand Down Expand Up @@ -48,4 +52,5 @@ export type CodeId = z.infer<typeof codeIdSchema>;

export const codeSearchSchema = z.object({
codeListId: codeListIdSchema,
allowEmptySelection: z.boolean().optional(),
});