Merge pull request #456 from Hestia-Homes/fix/install-doc-categorisation

fix(live-tracking): stop coordination and design docs counting as install docs
This commit is contained in:
Daniel Roth 2026-07-27 11:52:19 +01:00 committed by GitHub
commit 8377ef5146
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 84 additions and 12 deletions

View file

@ -216,6 +216,69 @@ describe("computeDocStatusMap", () => {
expect(result["deal-1"].installStatus).toBe("hasDocs");
});
it('is "none" when the only docs are coordination docs, which the drawer lists separately', () => {
// Arrange
const deal = makeDeal({ dealId: "deal-1", proposedMeasures: null });
const coordinationDocs = [
{ fileType: "improvement_option_evaluation", measureName: null },
];
const docs = new Map([["deal-1", coordinationDocs]]);
// Act
const result = computeDocStatusMap([deal], docs, {});
// Assert
expect(result["deal-1"].installStatus).toBe("none");
});
it('is "none" when the only docs are design docs, which the drawer lists separately', () => {
// Arrange
const deal = makeDeal({ dealId: "deal-1", proposedMeasures: null });
const designDocs = [{ fileType: "retrofit_design_doc", measureName: null }];
const docs = new Map([["deal-1", designDocs]]);
// Act
const result = computeDocStatusMap([deal], docs, {});
// Assert
expect(result["deal-1"].installStatus).toBe("none");
});
it("counts only genuine install docs when coordination and design docs are also present", () => {
// Arrange
const deal = makeDeal({ dealId: "deal-1", proposedMeasures: cwi });
const mixedDocs = [
{ fileType: "improvement_option_evaluation", measureName: null },
{ fileType: "retrofit_design_doc", measureName: null },
...cwiRequiredDocs.map((ft) => ({ fileType: ft, measureName: cwi })),
];
const docs = new Map([["deal-1", mixedDocs]]);
// Act
const result = computeDocStatusMap([deal], docs, {});
// Assert
expect(result["deal-1"].measureProgress).toEqual([
expect.objectContaining({ measureName: cwi, uploadedCount: cwiRequiredDocs.length }),
]);
});
it("reports no install docs when only coordination and design docs are present", () => {
// Arrange
const deal = makeDeal({ dealId: "deal-1", proposedMeasures: null });
const nonInstallDocs = [
{ fileType: "medium_term_improvement_plan", measureName: null },
{ fileType: "retrofit_design_doc", measureName: null },
];
const docs = new Map([["deal-1", nonInstallDocs]]);
// Act
const result = computeDocStatusMap([deal], docs, {});
// Assert
expect(result["deal-1"].hasInstallDocs).toBe(false);
});
it('is "none" when measures are defined but no install docs have been uploaded', () => {
const deal = makeDeal({ dealId: "deal-1", proposedMeasures: cwi });
// Only survey docs — no install docs

View file

@ -2,6 +2,7 @@ import { inArray } from "drizzle-orm";
import { db } from "@/app/db/db";
import { uploadedFiles } from "@/app/db/schema/uploaded_files";
import { getRequiredDocs } from "@/app/lib/measureDocumentRequirements";
import { isInstallDocType } from "./propertyDocuments";
import {
EXPECTED_RETROFIT_ASSESSMENT_DOC_TYPES,
SURVEY_ALL_DOC_TYPES,
@ -122,12 +123,12 @@ export function computeDocStatusMap(
for (const [dealId, docs] of docsByDealId) {
const surveyDocs = docs.filter((d) => SURVEY_ALL_DOC_TYPES.has(d.fileType));
// Design documents are their own category — they are neither survey nor
// install docs. Without this carve-out a Retrofit Design Document (absent
// from the survey set) would be mis-bucketed as an install document.
const installDocs = docs.filter(
(d) => !SURVEY_ALL_DOC_TYPES.has(d.fileType) && !DESIGN_DOC_TYPES.has(d.fileType),
);
// Survey, coordination and design docs each get their own drawer section,
// so none count as install docs. `isInstallDocType` is the shared predicate
// that keeps this in sync with the drawer's own split — re-implementing it
// inline once dropped the coordination carve-out and mis-bucketed
// coordination-only docs as install documents.
const installDocs = docs.filter((d) => isInstallDocType(d.fileType));
const surveyTypeSet = new Set(surveyDocs.map((d) => d.fileType));
const measures = measuresByDealId.get(dealId) ?? [];

View file

@ -6,6 +6,19 @@ import {
} from "./types";
import type { PropertyDocument, MeasureDocProgress } from "./types";
// Survey, coordination and design docs each get their own section in the
// property drawer, so none of them count as install docs. Kept as a single
// predicate shared with computeDocStatusMap: when the two drifted apart, the
// Install Docs badge in the properties table read "Has Docs" for properties
// whose drawer showed no install documents at all.
export function isInstallDocType(docType: string): boolean {
return (
!SURVEY_ALL_DOC_TYPES.has(docType) &&
!COORDINATION_DOC_TYPES.has(docType) &&
!DESIGN_DOC_TYPES.has(docType)
);
}
export function splitDocumentsByType(docs: PropertyDocument[]): {
docs: PropertyDocument[];
coordinationDocs: PropertyDocument[];
@ -16,12 +29,7 @@ export function splitDocumentsByType(docs: PropertyDocument[]): {
docs: docs.filter((d) => SURVEY_ALL_DOC_TYPES.has(d.docType)),
coordinationDocs: docs.filter((d) => COORDINATION_DOC_TYPES.has(d.docType)),
designDocs: docs.filter((d) => DESIGN_DOC_TYPES.has(d.docType)),
installDocs: docs.filter(
(d) =>
!SURVEY_ALL_DOC_TYPES.has(d.docType) &&
!COORDINATION_DOC_TYPES.has(d.docType) &&
!DESIGN_DOC_TYPES.has(d.docType),
),
installDocs: docs.filter((d) => isInstallDocType(d.docType)),
};
}