b17d7b063d
G11 수주 결재상신(905d5c09)과 동일 패턴을 견적관리에 확장. target_type='CONTRACT_ESTIMATE',
target_objid=estimate_template.objid(최신 차수), formId='1162' (수주 1161과 별도 양식).
- 백엔드: salesEstimateService.startEstimateApproval + POST /sales/estimate/:id/amaranth-approval
- 견적 list SQL: LEFT JOIN amaranth_approval(CONTRACT_ESTIMATE) + APPR_STATUS 4단계 한글 라벨 + approval_required='N' fallback (wace contractMgmt.xml:513~522 1:1)
- 프론트: 견적관리 placeholder 토스트 → handleAmaranthApproval 핸들러 + sky-600 Send 버튼 (수주 페이지와 통일)
- docker-compose 3개: AMARANTH_OUT_PROCESS_CODE_CONTRACT_ESTIMATE + AMARANTH_FORM_ID_CONTRACT_ESTIMATE=1162 추가
- 가드: 행 미선택 / est_objid 없음(견적서 미작성) / inProcess+complete / notRequired+approval_required='N'
- 사전판정(checkApprovalRequired)은 G4 영역으로 분리 — 이번 PR은 단순 SSO 흐름만
검증: BEGIN/ROLLBACK으로 26C-0712(est_objid=-452406811) 4단계 상태(create→inProcess→complete→reject)
+ amaranth row 삭제 시 approval_required='N' fallback 모두 한글 라벨 정상. 문서 08-estimate-approval-verify.md.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
312 lines
9.7 KiB
TypeScript
312 lines
9.7 KiB
TypeScript
import { apiClient } from "./client";
|
|
|
|
// ============================================================
|
|
// 영업관리 > 견적관리 API (wace_plm 도메인 이식)
|
|
// ============================================================
|
|
|
|
export interface EstimateListFilter {
|
|
category_cd?: string;
|
|
customer_objid?: string;
|
|
search_partNo?: string;
|
|
search_partObjId?: string;
|
|
search_partName?: string;
|
|
search_serialNo?: string;
|
|
appr_status?: string;
|
|
receipt_start_date?: string;
|
|
receipt_end_date?: string;
|
|
product?: string;
|
|
area_cd?: string;
|
|
paid_type?: string;
|
|
due_start_date?: string;
|
|
due_end_date?: string;
|
|
}
|
|
|
|
export interface EstimateRow {
|
|
objid: string;
|
|
contract_no: string | null;
|
|
category_cd: string | null;
|
|
category_name: string | null;
|
|
customer_objid: string | null;
|
|
customer_name: string | null;
|
|
customer_type: string | null;
|
|
product: string | null;
|
|
product_name: string | null;
|
|
area_cd: string | null;
|
|
area_name: string | null;
|
|
paid_type: string | null;
|
|
paid_type_name: string | null;
|
|
contract_result: string | null;
|
|
approval_required: string | null;
|
|
return_reason_summary: string | null;
|
|
contract_currency: string | null;
|
|
contract_currency_name: string | null;
|
|
exchange_rate: string | null;
|
|
receipt_date: string | null;
|
|
regdate: string | null;
|
|
writer: string | null;
|
|
writer_name: string | null;
|
|
pm_user_id: string | null;
|
|
pm_user_name: string | null;
|
|
est_objid: string | null;
|
|
estimate_no: string | null;
|
|
template_type: string | null;
|
|
est_total_amount: string | null;
|
|
est_total_amount_krw: string | null;
|
|
manager_name: string | null;
|
|
manager_contact: string | null;
|
|
estimate_quantity: number | null;
|
|
est_status: number | null;
|
|
item_summary: string | null;
|
|
part_no: string | null;
|
|
serial_no: string | null;
|
|
earliest_due_date: string | null;
|
|
other_due_date_count: number | null;
|
|
appr_status: string | null;
|
|
amaranth_status: string | null;
|
|
add_est_cnt: number | null;
|
|
cu01_cnt: number | null;
|
|
mail_send_status: string | null;
|
|
mail_send_date: string | null;
|
|
}
|
|
|
|
// wace estimateRegistFormPopup 폼 — 라인 8개 항목
|
|
export interface EstimateItem {
|
|
objid?: string;
|
|
seq: number;
|
|
product: string; // 제품구분 (필수)
|
|
part_objid: string; // 품목 마스터 id (필수)
|
|
part_no: string;
|
|
part_name: string;
|
|
serials?: string[]; // S/N 목록
|
|
quantity?: string; // 견적수량
|
|
due_date?: string; // 요청납기 (YYYY-MM-DD)
|
|
return_reason?: string; // 반납사유 (comm_code)
|
|
customer_request?: string; // 고객요청사항
|
|
}
|
|
|
|
// wace estimateRegistFormPopup 폼 — 헤더 8개 항목
|
|
export interface EstimateBody {
|
|
contract_no?: string; // 신규: 자동 채번 / 수정: 변경 안 함
|
|
category_cd: string; // 주문유형 *
|
|
area_cd: string; // 국내/해외 *
|
|
customer_objid: string; // 고객사 *
|
|
paid_type: string; // 유/무상 * ('paid' | 'free')
|
|
receipt_date: string; // 접수일 *
|
|
contract_currency?: string; // 견적환종
|
|
exchange_rate?: string; // 견적환율
|
|
approval_required: string; // 결재여부 * ('Y' | 'N')
|
|
items: EstimateItem[];
|
|
}
|
|
|
|
export const salesEstimateApi = {
|
|
async list(filter: EstimateListFilter = {}) {
|
|
const res = await apiClient.get("/sales/estimate/list", { params: filter });
|
|
return (res.data?.data ?? []) as EstimateRow[];
|
|
},
|
|
|
|
async detail(objid: string) {
|
|
const res = await apiClient.get(`/sales/estimate/${objid}`);
|
|
return res.data?.data;
|
|
},
|
|
|
|
async generateNumber(): Promise<string> {
|
|
const res = await apiClient.get("/sales/estimate/generate-number");
|
|
return res.data?.data?.estimateNo ?? "";
|
|
},
|
|
|
|
async create(body: EstimateBody) {
|
|
const res = await apiClient.post("/sales/estimate", body);
|
|
return res.data?.data as { objid: string; contract_no: string };
|
|
},
|
|
|
|
async update(objid: string, body: EstimateBody) {
|
|
const res = await apiClient.put(`/sales/estimate/${objid}`, body);
|
|
return res.data;
|
|
},
|
|
|
|
async remove(objid: string) {
|
|
const res = await apiClient.delete(`/sales/estimate/${objid}`);
|
|
return res.data;
|
|
},
|
|
|
|
async sendMail(body: {
|
|
contractObjid: string;
|
|
toEmails: string;
|
|
ccEmails?: string;
|
|
subject: string;
|
|
contents: string;
|
|
pdfBase64?: string;
|
|
useAddEstOnly?: "Y" | "N";
|
|
}) {
|
|
const res = await apiClient.post("/sales/estimate/mail", body, {
|
|
// PDF base64 포함 시 페이로드 큼 — 충분한 타임아웃
|
|
timeout: 120_000,
|
|
});
|
|
return res.data as { success: boolean; message: string; objid?: string };
|
|
},
|
|
|
|
/** 메일 다이얼로그 자동 채움 (제목/수신/참조용 고객·작성자 정보) */
|
|
async getMailInfo(contractObjid: string) {
|
|
const res = await apiClient.get(`/sales/estimate/mail-info/${contractObjid}`);
|
|
return res.data?.data as {
|
|
contract_objid: string;
|
|
contract_no: string | null;
|
|
customer_objid: string | null;
|
|
customer_name: string | null;
|
|
customer_email: string | null;
|
|
writer: string | null;
|
|
writer_email: string | null;
|
|
writer_name: string | null;
|
|
};
|
|
},
|
|
|
|
/** 메일 다이얼로그 고객사 담당자 체크박스 리스트용 */
|
|
async getMailManagers(customerObjid: string) {
|
|
const res = await apiClient.get(`/sales/estimate/customer/${customerObjid}/managers`);
|
|
return (res.data?.data ?? []) as {
|
|
name: string;
|
|
email: string;
|
|
phone: string;
|
|
department: string;
|
|
is_main: string;
|
|
}[];
|
|
},
|
|
|
|
// ─── G5 견적작성 (estimate_template) ──────────────────────────
|
|
async saveTemplate1(body: EstimateTemplate1Body) {
|
|
const res = await apiClient.post("/sales/estimate/template1", body);
|
|
return res.data?.data as { templateObjid: string; isUpdate: boolean };
|
|
},
|
|
|
|
async saveTemplate2(body: EstimateTemplate2Body) {
|
|
const res = await apiClient.post("/sales/estimate/template2", body);
|
|
return res.data?.data as { templateObjid: string; isUpdate: boolean };
|
|
},
|
|
|
|
async getTemplate(templateObjid: string) {
|
|
const res = await apiClient.get(`/sales/estimate/template/${templateObjid}`);
|
|
return res.data?.data as EstimateTemplateDetail | null;
|
|
},
|
|
|
|
async listTemplates(contractObjid: string) {
|
|
const res = await apiClient.get(`/sales/estimate/templates/${contractObjid}`);
|
|
return (res.data?.data ?? []) as EstimateTemplateRow[];
|
|
},
|
|
|
|
// G4/G11 동일 패턴 — 견적 결재상신 (Amaranth SSO URL 발급)
|
|
// wace estimateList_new.jsp:887 fn_openAmaranthApproval 1:1
|
|
async startApproval(objid: string, body: { approvalTitle?: string; subjectStr?: string } = {})
|
|
: Promise<{ fullUrl: string; approKey: string; status: string; estObjid: string }> {
|
|
const res = await apiClient.post(`/sales/estimate/${objid}/amaranth-approval`, body);
|
|
return res.data?.data;
|
|
},
|
|
};
|
|
|
|
// ─── G5 견적작성 타입 ───────────────────────────────────────────
|
|
|
|
export interface EstimateTemplateItemRow {
|
|
seq?: number;
|
|
category?: string | null;
|
|
part_objid?: string | null;
|
|
description?: string | null;
|
|
specification?: string | null;
|
|
quantity?: string | null;
|
|
unit?: string | null;
|
|
unit_price?: string | null;
|
|
amount?: string | null;
|
|
note?: string | null;
|
|
remark?: string | null;
|
|
}
|
|
|
|
// 일반(template1) 저장 페이로드 — wace estimateTemplate1.jsp fn_save
|
|
export interface EstimateTemplate1Body {
|
|
contract_objid: string;
|
|
template_objid?: string;
|
|
executor?: string;
|
|
recipient?: string;
|
|
estimate_no?: string;
|
|
contact_person?: string;
|
|
greeting_text?: string;
|
|
model_name?: string;
|
|
model_code?: string;
|
|
executor_date?: string;
|
|
note1?: string;
|
|
note2?: string;
|
|
note3?: string;
|
|
note4?: string;
|
|
note_remarks?: string;
|
|
total_amount?: string;
|
|
total_amount_krw?: string;
|
|
manager_name?: string;
|
|
manager_contact?: string;
|
|
show_total_row?: "Y" | "N";
|
|
items: EstimateTemplateItemRow[];
|
|
}
|
|
|
|
// 장비(template2) 저장 페이로드 — wace estimateTemplate2.jsp fn_save
|
|
export interface EstimateTemplate2Body {
|
|
contract_objid: string;
|
|
template_objid?: string;
|
|
executor_date?: string;
|
|
recipient?: string;
|
|
part_name?: string;
|
|
part_objid?: string;
|
|
notes_content?: string;
|
|
validity_period?: string;
|
|
categories_json: string;
|
|
group1_subtotal?: string;
|
|
total_amount?: string;
|
|
total_amount_krw?: string;
|
|
}
|
|
|
|
// 단건 조회 응답
|
|
export interface EstimateTemplateDetail {
|
|
objid: string;
|
|
contract_objid: string;
|
|
template_type: "1" | "2";
|
|
executor: string | null;
|
|
recipient: string | null;
|
|
estimate_no: string | null;
|
|
contact_person: string | null;
|
|
greeting_text: string | null;
|
|
model_name: string | null;
|
|
model_code: string | null;
|
|
executor_date: string | null;
|
|
note1: string | null;
|
|
note2: string | null;
|
|
note3: string | null;
|
|
note4: string | null;
|
|
note_remarks: string | null;
|
|
notes_content: string | null;
|
|
validity_period: string | null;
|
|
categories_json: string | null;
|
|
group1_subtotal: string | null;
|
|
total_amount: string | null;
|
|
total_amount_krw: string | null;
|
|
manager_name: string | null;
|
|
manager_contact: string | null;
|
|
show_total_row: string | null;
|
|
part_name: string | null;
|
|
part_objid: string | null;
|
|
writer: string | null;
|
|
regdate_str: string | null;
|
|
chgdate_str: string | null;
|
|
exchange_rate: string | null;
|
|
contract_currency: string | null;
|
|
contract_currency_name: string | null;
|
|
items: EstimateTemplateItemRow[];
|
|
}
|
|
|
|
// 차수 리스트 행
|
|
export interface EstimateTemplateRow {
|
|
objid: string;
|
|
template_type: "1" | "2";
|
|
estimate_no: string | null;
|
|
recipient: string | null;
|
|
total_amount: string | null;
|
|
total_amount_krw: string | null;
|
|
writer: string | null;
|
|
regdate: string | null;
|
|
chgdate: string | null;
|
|
}
|