251213:1509 Docunment Number Businee Rule not correct
This commit is contained in:
@@ -28,7 +28,7 @@ export function CorrespondenceDetail({ data }: CorrespondenceDetailProps) {
|
||||
|
||||
// Derive Current Revision Data
|
||||
const currentRevision = data.revisions?.find(r => r.isCurrent) || data.revisions?.[0];
|
||||
const subject = currentRevision?.title || "-";
|
||||
const subject = currentRevision?.subject || "-";
|
||||
const description = currentRevision?.description || "-";
|
||||
const status = currentRevision?.status?.statusCode || "UNKNOWN"; // e.g. DRAFT
|
||||
const attachments = currentRevision?.attachments || [];
|
||||
@@ -169,6 +169,24 @@ export function CorrespondenceDetail({ data }: CorrespondenceDetailProps) {
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{currentRevision?.body && (
|
||||
<div>
|
||||
<h3 className="font-semibold mb-2">Content</h3>
|
||||
<div className="text-gray-700 whitespace-pre-wrap p-3 bg-muted/10 rounded-md border">
|
||||
{currentRevision.body}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{currentRevision?.remarks && (
|
||||
<div>
|
||||
<h3 className="font-semibold mb-2">Remarks</h3>
|
||||
<p className="text-gray-600 italic">
|
||||
{currentRevision.remarks}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<hr className="my-4 border-t" />
|
||||
|
||||
<div>
|
||||
@@ -223,8 +241,8 @@ export function CorrespondenceDetail({ data }: CorrespondenceDetailProps) {
|
||||
|
||||
<div>
|
||||
<p className="text-sm font-medium text-muted-foreground">Originator</p>
|
||||
<p className="font-medium mt-1">{data.originator?.orgName || '-'}</p>
|
||||
<p className="text-xs text-muted-foreground">{data.originator?.orgCode || '-'}</p>
|
||||
<p className="font-medium mt-1">{data.originator?.organizationName || '-'}</p>
|
||||
<p className="text-xs text-muted-foreground">{data.originator?.organizationCode || '-'}</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
||||
@@ -21,6 +21,8 @@ import { useCreateCorrespondence, useUpdateCorrespondence } from "@/hooks/use-co
|
||||
import { Organization } from "@/types/organization";
|
||||
import { useOrganizations, useProjects, useCorrespondenceTypes, useDisciplines } from "@/hooks/use-master-data";
|
||||
import { CreateCorrespondenceDto } from "@/types/dto/correspondence/create-correspondence.dto";
|
||||
import { useState, useEffect } from "react";
|
||||
import { correspondenceService } from "@/lib/services/correspondence.service";
|
||||
|
||||
// Updated Zod Schema with all required fields
|
||||
const correspondenceSchema = z.object({
|
||||
@@ -29,6 +31,9 @@ const correspondenceSchema = z.object({
|
||||
disciplineId: z.number().optional(),
|
||||
subject: z.string().min(5, "Subject must be at least 5 characters"),
|
||||
description: z.string().optional(),
|
||||
body: z.string().optional(),
|
||||
remarks: z.string().optional(),
|
||||
dueDate: z.string().optional(), // ISO Date string
|
||||
fromOrganizationId: z.number().min(1, "Please select From Organization"),
|
||||
toOrganizationId: z.number().min(1, "Please select To Organization"),
|
||||
importance: z.enum(["NORMAL", "HIGH", "URGENT"]),
|
||||
@@ -54,10 +59,14 @@ export function CorrespondenceForm({ initialData, id }: { initialData?: any, id?
|
||||
projectId: initialData?.projectId || undefined,
|
||||
documentTypeId: initialData?.correspondenceTypeId || undefined,
|
||||
disciplineId: initialData?.disciplineId || undefined,
|
||||
subject: currentRev?.title || "",
|
||||
subject: currentRev?.subject || currentRev?.title || "",
|
||||
description: currentRev?.description || "",
|
||||
body: currentRev?.body || "",
|
||||
remarks: currentRev?.remarks || "",
|
||||
dueDate: currentRev?.dueDate ? new Date(currentRev.dueDate).toISOString().split('T')[0] : undefined,
|
||||
fromOrganizationId: initialData?.originatorId || undefined,
|
||||
toOrganizationId: currentRev?.details?.to_organization_id || undefined,
|
||||
// Map initial recipient (TO) - Simplified for now
|
||||
toOrganizationId: initialData?.recipients?.find((r: any) => r.recipientType === 'TO')?.recipientOrganizationId || undefined,
|
||||
importance: currentRev?.details?.importance || "NORMAL",
|
||||
};
|
||||
|
||||
@@ -84,11 +93,16 @@ export function CorrespondenceForm({ initialData, id }: { initialData?: any, id?
|
||||
projectId: data.projectId,
|
||||
typeId: data.documentTypeId,
|
||||
disciplineId: data.disciplineId,
|
||||
title: data.subject,
|
||||
subject: data.subject,
|
||||
description: data.description,
|
||||
body: data.body,
|
||||
remarks: data.remarks,
|
||||
dueDate: data.dueDate ? new Date(data.dueDate).toISOString() : undefined,
|
||||
originatorId: data.fromOrganizationId,
|
||||
recipients: [
|
||||
{ organizationId: data.toOrganizationId, type: 'TO' }
|
||||
],
|
||||
details: {
|
||||
to_organization_id: data.toOrganizationId,
|
||||
importance: data.importance
|
||||
},
|
||||
};
|
||||
@@ -108,8 +122,56 @@ export function CorrespondenceForm({ initialData, id }: { initialData?: any, id?
|
||||
|
||||
const isPending = createMutation.isPending || updateMutation.isPending;
|
||||
|
||||
// -- Preview Logic --
|
||||
const [preview, setPreview] = useState<{ number: string; isDefaultTemplate: boolean } | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!projectId || !documentTypeId || !fromOrgId || !toOrgId) {
|
||||
setPreview(null);
|
||||
return;
|
||||
}
|
||||
|
||||
const fetchPreview = async () => {
|
||||
try {
|
||||
const res = await correspondenceService.previewNumber({
|
||||
projectId,
|
||||
typeId: documentTypeId,
|
||||
disciplineId,
|
||||
originatorId: fromOrgId,
|
||||
// Map recipients structure matching backend expectation
|
||||
recipients: [{ organizationId: toOrgId, type: 'TO' }],
|
||||
// Add date just to be safe, though service uses 'now'
|
||||
dueDate: new Date().toISOString()
|
||||
});
|
||||
setPreview(res);
|
||||
} catch (err) {
|
||||
setPreview(null);
|
||||
}
|
||||
};
|
||||
|
||||
const timer = setTimeout(fetchPreview, 500);
|
||||
return () => clearTimeout(timer);
|
||||
}, [projectId, documentTypeId, disciplineId, fromOrgId, toOrgId]);
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit(onSubmit)} className="max-w-3xl space-y-6">
|
||||
{/* Preview Section */}
|
||||
{preview && (
|
||||
<div className="p-4 rounded-md bg-muted border border-border">
|
||||
<p className="text-sm text-muted-foreground mb-1">Document Number Preview</p>
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="text-xl font-bold font-mono text-primary tracking-wide">{preview.number}</span>
|
||||
{preview.isDefaultTemplate && (
|
||||
<span className="text-[10px] uppercase font-semibold px-2 py-0.5 rounded-full bg-yellow-100 text-yellow-800 border border-yellow-200">
|
||||
Default Template
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Document Metadata Section */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
{/* Project Dropdown */}
|
||||
@@ -191,14 +253,37 @@ export function CorrespondenceForm({ initialData, id }: { initialData?: any, id?
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Body */}
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="body">Body (Content)</Label>
|
||||
<Textarea
|
||||
id="body"
|
||||
{...register("body")}
|
||||
rows={6}
|
||||
placeholder="Enter letter content..."
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Remarks & Due Date */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="remarks">Remarks</Label>
|
||||
<Input id="remarks" {...register("remarks")} placeholder="Optional remarks" />
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="dueDate">Due Date</Label>
|
||||
<Input id="dueDate" type="date" {...register("dueDate")} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Description */}
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="description">Description</Label>
|
||||
<Label htmlFor="description">Description (Internal Note)</Label>
|
||||
<Textarea
|
||||
id="description"
|
||||
{...register("description")}
|
||||
rows={4}
|
||||
placeholder="Enter description details..."
|
||||
rows={2}
|
||||
placeholder="Enter description..."
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -30,11 +30,11 @@ export function CorrespondenceList({ data }: CorrespondenceListProps) {
|
||||
),
|
||||
},
|
||||
{
|
||||
accessorKey: "title",
|
||||
accessorKey: "subject",
|
||||
header: "Subject",
|
||||
cell: ({ row }) => (
|
||||
<div className="max-w-[300px] truncate" title={row.original.title}>
|
||||
{row.original.title}
|
||||
<div className="max-w-[300px] truncate" title={row.original.subject}>
|
||||
{row.original.subject}
|
||||
</div>
|
||||
),
|
||||
},
|
||||
|
||||
@@ -5,6 +5,7 @@ import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { z } from "zod";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Card } from "@/components/ui/card";
|
||||
import { Plus, Trash2, Loader2 } from "lucide-react";
|
||||
@@ -19,6 +20,8 @@ import { useRouter } from "next/navigation";
|
||||
import { useCreateRFA } from "@/hooks/use-rfa";
|
||||
import { useDisciplines, useContracts } from "@/hooks/use-master-data";
|
||||
import { CreateRFADto } from "@/types/rfa";
|
||||
import { useState, useEffect } from "react";
|
||||
import { correspondenceService } from "@/lib/services/correspondence.service";
|
||||
|
||||
const rfaItemSchema = z.object({
|
||||
itemNo: z.string().min(1, "Item No is required"),
|
||||
@@ -30,8 +33,10 @@ const rfaSchema = z.object({
|
||||
contractId: z.number().min(1, "Contract is required"),
|
||||
disciplineId: z.number().min(1, "Discipline is required"),
|
||||
rfaTypeId: z.number().min(1, "Type is required"),
|
||||
title: z.string().min(5, "Title must be at least 5 characters"),
|
||||
subject: z.string().min(5, "Subject must be at least 5 characters"),
|
||||
description: z.string().optional(),
|
||||
body: z.string().optional(),
|
||||
remarks: z.string().optional(),
|
||||
toOrganizationId: z.number().min(1, "Please select To Organization"),
|
||||
dueDate: z.string().optional(),
|
||||
shopDrawingRevisionIds: z.array(z.number()).optional(),
|
||||
@@ -61,8 +66,10 @@ export function RFAForm() {
|
||||
contractId: 0,
|
||||
disciplineId: 0,
|
||||
rfaTypeId: 0,
|
||||
title: "",
|
||||
subject: "",
|
||||
description: "",
|
||||
body: "",
|
||||
remarks: "",
|
||||
toOrganizationId: 0,
|
||||
dueDate: "",
|
||||
shopDrawingRevisionIds: [],
|
||||
@@ -73,6 +80,40 @@ export function RFAForm() {
|
||||
const selectedContractId = watch("contractId");
|
||||
const { data: disciplines, isLoading: isLoadingDisciplines } = useDisciplines(selectedContractId);
|
||||
|
||||
// Watch fields for preview
|
||||
const rfaTypeId = watch("rfaTypeId");
|
||||
const disciplineId = watch("disciplineId");
|
||||
const toOrganizationId = watch("toOrganizationId");
|
||||
|
||||
// -- Preview Logic --
|
||||
const [preview, setPreview] = useState<{ number: string; isDefaultTemplate: boolean } | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!rfaTypeId || !disciplineId || !toOrganizationId) {
|
||||
setPreview(null);
|
||||
return;
|
||||
}
|
||||
|
||||
const fetchPreview = async () => {
|
||||
try {
|
||||
const res = await correspondenceService.previewNumber({
|
||||
projectId: currentProjectId,
|
||||
typeId: rfaTypeId, // RfaTypeId acts as TypeId
|
||||
disciplineId,
|
||||
// RFA uses 'TO' organization as recipient
|
||||
recipients: [{ organizationId: toOrganizationId, type: 'TO' }],
|
||||
dueDate: new Date().toISOString()
|
||||
});
|
||||
setPreview(res);
|
||||
} catch (err) {
|
||||
setPreview(null);
|
||||
}
|
||||
};
|
||||
|
||||
const timer = setTimeout(fetchPreview, 500);
|
||||
return () => clearTimeout(timer);
|
||||
}, [rfaTypeId, disciplineId, toOrganizationId, currentProjectId]);
|
||||
|
||||
const { fields, append, remove } = useFieldArray({
|
||||
control,
|
||||
name: "items",
|
||||
@@ -92,24 +133,49 @@ export function RFAForm() {
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit(onSubmit)} className="max-w-4xl space-y-6">
|
||||
{/* Preview Section */}
|
||||
{preview && (
|
||||
<Card className="p-4 bg-muted border-l-4 border-l-primary">
|
||||
<p className="text-sm text-muted-foreground mb-1">Document Number Preview</p>
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="text-xl font-bold font-mono text-primary tracking-wide">{preview.number}</span>
|
||||
{preview.isDefaultTemplate && (
|
||||
<span className="text-[10px] uppercase font-semibold px-2 py-0.5 rounded-full bg-yellow-100 text-yellow-800 border border-yellow-200">
|
||||
Default Template
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
{/* Basic Info */}
|
||||
<Card className="p-6">
|
||||
<h3 className="text-lg font-semibold mb-4">RFA Information</h3>
|
||||
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<Label htmlFor="title">Title *</Label>
|
||||
<Input id="title" {...register("title")} placeholder="Enter title" />
|
||||
{errors.title && (
|
||||
<Label htmlFor="subject">Subject *</Label>
|
||||
<Input id="subject" {...register("subject")} placeholder="Enter subject" />
|
||||
{errors.subject && (
|
||||
<p className="text-sm text-destructive mt-1">
|
||||
{errors.title.message}
|
||||
{errors.subject.message}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label htmlFor="body">Body (Content)</Label>
|
||||
<Textarea id="body" {...register("body")} rows={4} placeholder="Enter content..." />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label htmlFor="remarks">Remarks</Label>
|
||||
<Input id="remarks" {...register("remarks")} placeholder="Optional remarks" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label htmlFor="description">Description</Label>
|
||||
<Input id="description" {...register("description")} placeholder="Enter description" />
|
||||
<Input id="description" {...register("description")} placeholder="Enter key description" />
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
|
||||
@@ -21,8 +21,7 @@ export function RFAList({ data }: RFAListProps) {
|
||||
accessorKey: "rfa_number",
|
||||
header: "RFA No.",
|
||||
cell: ({ row }) => {
|
||||
const rev = row.original.revisions?.[0];
|
||||
return <span className="font-medium">{rev?.correspondence?.correspondenceNumber || '-'}</span>;
|
||||
return <span className="font-medium">{row.original.correspondence?.correspondenceNumber || '-'}</span>;
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -31,8 +30,8 @@ export function RFAList({ data }: RFAListProps) {
|
||||
cell: ({ row }) => {
|
||||
const rev = row.original.revisions?.[0];
|
||||
return (
|
||||
<div className="max-w-[300px] truncate" title={rev?.title}>
|
||||
{rev?.title || '-'}
|
||||
<div className="max-w-[300px] truncate" title={rev?.subject}>
|
||||
{rev?.subject || '-'}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
@@ -41,8 +40,7 @@ export function RFAList({ data }: RFAListProps) {
|
||||
accessorKey: "contract_name", // AccessorKey can be anything if we provide cell
|
||||
header: "Contract",
|
||||
cell: ({ row }) => {
|
||||
const rev = row.original.revisions?.[0];
|
||||
return <span>{rev?.correspondence?.project?.projectName || '-'}</span>;
|
||||
return <span>{row.original.correspondence?.project?.projectName || '-'}</span>;
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -54,7 +52,10 @@ export function RFAList({ data }: RFAListProps) {
|
||||
accessorKey: "createdAt",
|
||||
header: "Created",
|
||||
cell: ({ row }) => {
|
||||
const date = row.original.revisions?.[0]?.correspondence?.createdAt;
|
||||
const date = row.original.correspondence?.createdAt || row.original.revisions?.[0]?.createdAt; // Fallback or strict?
|
||||
// In backend I set RFA -> Correspondence (createdAt is in Correspondence base)
|
||||
// But RFA revision also has createdAt?
|
||||
// Use correspondence.createdAt usually for document date.
|
||||
return date ? format(new Date(date), "dd MMM yyyy") : '-';
|
||||
},
|
||||
},
|
||||
|
||||
@@ -70,5 +70,12 @@ export const correspondenceService = {
|
||||
data: data
|
||||
});
|
||||
return response.data;
|
||||
},
|
||||
/**
|
||||
* Preview Document Number
|
||||
*/
|
||||
previewNumber: async (data: Partial<CreateCorrespondenceDto>) => {
|
||||
const response = await apiClient.post("/correspondences/preview-number", data);
|
||||
return response.data;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -18,7 +18,11 @@ export interface CorrespondenceRevision {
|
||||
id: number;
|
||||
revisionNumber: number;
|
||||
revisionLabel?: string; // e.g. "A", "00"
|
||||
title: string;
|
||||
subject: string;
|
||||
body?: string;
|
||||
remarks?: string;
|
||||
dueDate?: string;
|
||||
schemaVersion?: number;
|
||||
description?: string;
|
||||
isCurrent: boolean;
|
||||
status?: {
|
||||
@@ -40,7 +44,7 @@ export interface CorrespondenceRevision {
|
||||
originator?: Organization;
|
||||
project?: { id: number; projectName: string; projectCode: string };
|
||||
type?: { id: number; typeName: string; typeCode: string };
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Keep explicit Correspondence for Detail View if needed, or merge concepts
|
||||
@@ -58,14 +62,27 @@ export interface Correspondence {
|
||||
project?: { id: number; projectName: string; projectCode: string };
|
||||
type?: { id: number; typeName: string; typeCode: string };
|
||||
revisions?: CorrespondenceRevision[]; // Nested revisions
|
||||
recipients?: {
|
||||
correspondenceId: number;
|
||||
recipientOrganizationId: number;
|
||||
recipientType: 'TO' | 'CC';
|
||||
recipientOrganization?: Organization;
|
||||
}[];
|
||||
}
|
||||
|
||||
export interface CreateCorrespondenceDto {
|
||||
projectId: number;
|
||||
typeId: number;
|
||||
subTypeId?: number;
|
||||
disciplineId?: number;
|
||||
subject: string;
|
||||
body?: string;
|
||||
remarks?: string;
|
||||
dueDate?: string;
|
||||
description?: string;
|
||||
documentTypeId: number;
|
||||
fromOrganizationId: number;
|
||||
toOrganizationId: number;
|
||||
importance: "NORMAL" | "HIGH" | "URGENT";
|
||||
details?: Record<string, any>;
|
||||
isInternal?: boolean;
|
||||
originatorId?: number;
|
||||
recipients?: { organizationId: number; type: 'TO' | 'CC' }[];
|
||||
attachments?: File[];
|
||||
}
|
||||
|
||||
@@ -5,20 +5,29 @@ export interface CreateCorrespondenceDto {
|
||||
projectId: number;
|
||||
|
||||
/** ID ของประเภทเอกสาร (เช่น RFA, LETTER) */
|
||||
typeId: number;
|
||||
|
||||
typeId: number;
|
||||
|
||||
/** [Req 6B] สาขางาน (เช่น GEN, STR) */
|
||||
disciplineId?: number;
|
||||
disciplineId?: number;
|
||||
|
||||
/** [Req 6B] ประเภทย่อย (เช่น MAT, SHP สำหรับ Transmittal/RFA) */
|
||||
subTypeId?: number;
|
||||
|
||||
|
||||
/** หัวข้อเอกสาร */
|
||||
title: string;
|
||||
subject: string;
|
||||
|
||||
/** รายละเอียดเพิ่มเติม (Optional) */
|
||||
description?: string;
|
||||
|
||||
/** เนื้อหาเอกสาร (Rich Text) */
|
||||
body?: string;
|
||||
|
||||
/** หมายเหตุ */
|
||||
remarks?: string;
|
||||
|
||||
/** กำหนดวันตอบกลับ (ISO Date String) */
|
||||
dueDate?: string;
|
||||
|
||||
/** ข้อมูล JSON เฉพาะประเภท (เช่น RFI question, RFA details) */
|
||||
details?: Record<string, any>;
|
||||
|
||||
@@ -29,4 +38,7 @@ export interface CreateCorrespondenceDto {
|
||||
* ใช้กรณี Admin สร้างเอกสารแทนผู้อื่น
|
||||
*/
|
||||
originatorId?: number;
|
||||
}
|
||||
|
||||
/** รายชื่อผู้รับ */
|
||||
recipients?: { organizationId: number; type: 'TO' | 'CC' }[];
|
||||
}
|
||||
|
||||
@@ -12,7 +12,13 @@ export interface CreateRfaDto {
|
||||
disciplineId?: number;
|
||||
|
||||
/** หัวข้อเรื่อง */
|
||||
title: string;
|
||||
subject: string;
|
||||
|
||||
/** เนื้อหา (Rich Text) */
|
||||
body?: string;
|
||||
|
||||
/** หมายเหตุ */
|
||||
remarks?: string;
|
||||
|
||||
/** ส่งถึงใคร (สำหรับ Routing Step 1) */
|
||||
toOrganizationId: number;
|
||||
|
||||
@@ -8,13 +8,22 @@ export interface RFAItem {
|
||||
}
|
||||
|
||||
export interface RFA {
|
||||
id: number;
|
||||
id: number; // Shared PK with Correspondence
|
||||
rfaTypeId: number;
|
||||
createdBy: number;
|
||||
disciplineId?: number;
|
||||
revisions: {
|
||||
id: number;
|
||||
revisionNumber: number;
|
||||
subject: string;
|
||||
isCurrent: boolean;
|
||||
createdAt?: string;
|
||||
statusCode?: { statusCode: string; statusName: string };
|
||||
items?: {
|
||||
shopDrawingRevision?: {
|
||||
id: number;
|
||||
revisionLabel: string;
|
||||
shopDrawing?: { drawingType?: { hasNumber: boolean } }; // Mock structure
|
||||
attachments?: { id: number; url: string; name: string }[]
|
||||
}
|
||||
}[];
|
||||
@@ -24,25 +33,33 @@ export interface RFA {
|
||||
name: string;
|
||||
code: string;
|
||||
};
|
||||
// Deprecated/Mapped fields (keep optional if frontend uses them elsewhere)
|
||||
rfaId?: number;
|
||||
rfaNumber?: string;
|
||||
subject?: string;
|
||||
status?: string;
|
||||
createdAt?: string;
|
||||
contractName?: string;
|
||||
disciplineName?: string;
|
||||
// Shared Correspondence Relation
|
||||
correspondence?: {
|
||||
id: number;
|
||||
correspondenceNumber: string;
|
||||
projectId: number;
|
||||
originatorId?: number;
|
||||
createdAt?: string;
|
||||
project?: {
|
||||
projectName: string;
|
||||
projectCode: string;
|
||||
};
|
||||
};
|
||||
|
||||
// Deprecated/Mapped fields
|
||||
correspondenceNumber?: string; // Convenience accessor
|
||||
}
|
||||
|
||||
export interface CreateRFADto {
|
||||
projectId?: number;
|
||||
projectId: number;
|
||||
rfaTypeId: number;
|
||||
title: string;
|
||||
disciplineId?: number;
|
||||
subject: string;
|
||||
body?: string; // [New]
|
||||
remarks?: string; // [New]
|
||||
dueDate?: string; // [New]
|
||||
description?: string;
|
||||
contractId: number;
|
||||
disciplineId: number;
|
||||
toOrganizationId: number;
|
||||
dueDate?: string;
|
||||
documentDate?: string;
|
||||
details?: Record<string, any>;
|
||||
shopDrawingRevisionIds?: number[];
|
||||
items: RFAItem[];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user