17b08c7a09
- backend purchaseOrderMailService 신설 — getOrderMailInfo / getPartnerManagerList / sendOrderMail (SMTP PURCHASE, 발송 성공 시 mail_send_yn='Y'/mail_send_date 갱신) - backend routes — GET /order-form/mail-info/:objid, POST /order-form/mail, GET /options/partner-managers/:partnerObjid - frontend lib/utils/purchaseOrderPdf — html2canvas-pro + jsPDF (A4, scale=2, input/textarea → 텍스트 변환). download:true 면 파일 저장, 아니면 base64 반환 - PurchaseOrderMailDialog 신설 — EstimateMailDialog 패턴 단순화 (한글/영문 본문 분기, 공급업체 단일 email 자동 채움) - 3개 양식 다이얼로그 — 읽기전용 + 저장된 발주서일 때 "메일 발송" + "PDF 다운로드" 버튼 노출. window.print 간이판 제거 - 3개 양식 다이얼로그 — "행 추가"/"선택 행 삭제" 버튼 + 그리드 체크박스 컬럼 제거 (wace 운영판은 모두 주석 처리/부재. 발주서는 품의서에서 자동 채움된 품목 그대로 사용)
186 lines
6.4 KiB
TypeScript
186 lines
6.4 KiB
TypeScript
// ============================================================
|
|
// 구매관리 — 7개 메뉴 그리드 API.
|
|
// 백엔드: /api/purchase/{menu-path}, /api/purchase/options/{suppliers|users|projects}
|
|
// ============================================================
|
|
|
|
import { apiClient } from "./client";
|
|
|
|
export interface PurchaseListFilter {
|
|
year?: string;
|
|
customer_objid?: string;
|
|
customer_cd?: string;
|
|
project_no?: string;
|
|
part_no?: string;
|
|
part_name?: string;
|
|
part_spec?: string;
|
|
partner_objid?: string;
|
|
purchase_order_no?: string;
|
|
proposal_no?: string;
|
|
search_status?: string;
|
|
writer?: string;
|
|
request_user?: string;
|
|
purchase_type?: string;
|
|
part_type?: string;
|
|
product_cd?: string;
|
|
category_cd?: string;
|
|
paid_type?: string;
|
|
mail_send_yn?: string;
|
|
delivery_status?: string;
|
|
close_status?: string;
|
|
sales_mng_user_id?: string;
|
|
regdate_start?: string;
|
|
regdate_end?: string;
|
|
receipt_date_start?: string;
|
|
receipt_date_end?: string;
|
|
delivery_start_date?: string;
|
|
delivery_end_date?: string;
|
|
reg_start_date?: string;
|
|
reg_end_date?: string;
|
|
page?: number;
|
|
page_size?: number;
|
|
}
|
|
|
|
export interface PurchaseListResponse<T = any> {
|
|
rows: T[];
|
|
totalCount: number;
|
|
page: number;
|
|
pageSize: number;
|
|
}
|
|
|
|
export interface OptionItem {
|
|
code: string;
|
|
label: string;
|
|
}
|
|
|
|
async function getList<T = any>(path: string, filter: PurchaseListFilter): Promise<PurchaseListResponse<T>> {
|
|
const res = await apiClient.get(`/purchase/${path}`, { params: filter });
|
|
return res.data?.data as PurchaseListResponse<T>;
|
|
}
|
|
|
|
export interface OrderFormData {
|
|
master: Record<string, any>;
|
|
parts: Record<string, any>[];
|
|
}
|
|
|
|
export interface SaveOrderPayload {
|
|
master: Record<string, any>;
|
|
parts: Record<string, any>[];
|
|
deletedPartObjids?: string[];
|
|
}
|
|
|
|
export interface SaveOrderResult {
|
|
objid: string;
|
|
purchase_order_no: string;
|
|
}
|
|
|
|
export interface OrderMailInfo {
|
|
pom_objid: string;
|
|
purchase_order_no: string;
|
|
partner_objid: string;
|
|
partner_name: string;
|
|
partner_email: string;
|
|
writer_email: string;
|
|
writer_name: string;
|
|
form_type: string;
|
|
}
|
|
|
|
export interface PartnerManager {
|
|
name: string;
|
|
email: string;
|
|
phone: string;
|
|
department: string;
|
|
is_main: string;
|
|
}
|
|
|
|
export interface SendOrderMailPayload {
|
|
pomObjid: string;
|
|
toEmails: string;
|
|
ccEmails?: string;
|
|
subject: string;
|
|
contents: string;
|
|
pdfBase64: string;
|
|
}
|
|
|
|
export const purchaseApi = {
|
|
// 그리드 7종
|
|
listPurchaseRequest: (f: PurchaseListFilter = {}) => getList("purchase-request", f),
|
|
listQuotationRequest: (f: PurchaseListFilter = {}) => getList("quotation-request", f),
|
|
listProposal: (f: PurchaseListFilter = {}) => getList("proposal", f),
|
|
listInbound: (f: PurchaseListFilter = {}) => getList("inbound", f),
|
|
listInboundByItem: (f: PurchaseListFilter = {}) => getList("inbound-by-item", f),
|
|
listInboundByDate: (f: PurchaseListFilter = {}) => getList("inbound-by-date", f),
|
|
listProjectStatus: (f: PurchaseListFilter = {}) => getList("project-status", f),
|
|
listOrder: (f: PurchaseListFilter = {}) => getList("order-list", f),
|
|
|
|
// ─── 발주서 폼 (general 양식) ──────────────────────────
|
|
/** 품의서 OBJID 로 발주서 폼 초기값 + 품목 자동채움. */
|
|
async initOrderForm(proposalObjid: string): Promise<OrderFormData> {
|
|
const r = await apiClient.get("/purchase/order-form/init", {
|
|
params: { proposal_objid: proposalObjid },
|
|
});
|
|
return r.data?.data as OrderFormData;
|
|
},
|
|
/** 기존 발주서 마스터+파트 조회 (수정 모드). */
|
|
async getOrderForm(objid: string): Promise<OrderFormData> {
|
|
const r = await apiClient.get(`/purchase/order-form/${encodeURIComponent(objid)}`);
|
|
return r.data?.data as OrderFormData;
|
|
},
|
|
/** 마스터+파트 UPSERT + 누락파트 삭제. */
|
|
async saveOrderForm(payload: SaveOrderPayload): Promise<SaveOrderResult> {
|
|
const r = await apiClient.post("/purchase/order-form/save", payload);
|
|
return r.data?.data as SaveOrderResult;
|
|
},
|
|
/** 발주서 삭제 (cascade). */
|
|
async deleteOrderForm(objid: string): Promise<void> {
|
|
await apiClient.delete(`/purchase/order-form/${encodeURIComponent(objid)}`);
|
|
},
|
|
|
|
// ─── 발주서 메일 발송 ─────────────────────────────────────
|
|
/** 메일 다이얼로그 자동채움 — 공급업체 이메일/이름 + 작성자 이메일 + 발주번호 */
|
|
async getOrderMailInfo(pomObjid: string): Promise<OrderMailInfo | null> {
|
|
const r = await apiClient.get(`/purchase/order-form/mail-info/${encodeURIComponent(pomObjid)}`);
|
|
return (r.data?.data ?? null) as OrderMailInfo | null;
|
|
},
|
|
/** 공급업체 담당자 리스트 — RPS client_mng 단일 email */
|
|
async listPartnerManagers(partnerObjid: string): Promise<PartnerManager[]> {
|
|
const r = await apiClient.get(
|
|
`/purchase/options/partner-managers/${encodeURIComponent(partnerObjid)}`,
|
|
);
|
|
return (r.data?.data ?? []) as PartnerManager[];
|
|
},
|
|
/** PDF 첨부 + SMTP 발송. 성공 시 mail_send_yn='Y'/mail_send_date=NOW() 갱신. */
|
|
async sendOrderMail(payload: SendOrderMailPayload): Promise<{ success: boolean; message: string; objid?: string }> {
|
|
const r = await apiClient.post("/purchase/order-form/mail", payload);
|
|
return r.data as { success: boolean; message: string; objid?: string };
|
|
},
|
|
|
|
// 공통 옵션
|
|
async listSuppliers(): Promise<OptionItem[]> {
|
|
const r = await apiClient.get("/purchase/options/suppliers");
|
|
return (r.data?.data ?? []) as OptionItem[];
|
|
},
|
|
// 견적요청서 / 발주서 vendor (wace client_mng 매칭)
|
|
async listVendors(): Promise<OptionItem[]> {
|
|
const r = await apiClient.get("/purchase/options/vendors");
|
|
return (r.data?.data ?? []) as OptionItem[];
|
|
},
|
|
async listUsers(): Promise<OptionItem[]> {
|
|
const r = await apiClient.get("/purchase/options/users");
|
|
return (r.data?.data ?? []) as OptionItem[];
|
|
},
|
|
async listProjects(): Promise<OptionItem[]> {
|
|
const r = await apiClient.get("/purchase/options/projects");
|
|
return (r.data?.data ?? []) as OptionItem[];
|
|
},
|
|
};
|
|
|
|
/** 년도 옵션 — wace 운영판 동일 (현재년도 ±4) */
|
|
export function getYearOptions(): OptionItem[] {
|
|
const y = new Date().getFullYear();
|
|
const out: OptionItem[] = [];
|
|
for (let i = y + 4; i >= y - 4; i--) {
|
|
out.push({ code: String(i), label: String(i) });
|
|
}
|
|
return out;
|
|
}
|