Files
wace_rps/frontend/lib/api/purchase.ts
T
hjjeong e51f5f7b69 구매관리 입고관리 입고등록 + 입고일별 마감정보입력 + 매입마감
- backend purchaseInboundService 신설 — getInboundFormInit / saveInboundForm
  (arrival_plan UPSERT 트랜잭션) / saveDeadlineInfo (8필드 일괄 UPDATE) /
  closeArrival (이미 마감된 건 차단) + listWarehouseOptions / listAcctCodeOptions
- backend routes — GET /inbound-form/:pomObjid / POST /inbound-form/save /
  POST /arrival/deadline / POST /arrival/close + 옵션 2개
- InboundFormDialog 신설 — wace deliveryAcceptanceFormPopUp_new.jsp 1:1
  (좌 발주품목 read-only + 우 차수별 입고입력 + 미입고 일괄적용)
- DeadlineInfoDialog 신설 — wace swal 모달 1:1 (8필드 일괄, 단건 시 prefill)
- inbound 페이지 입고등록 / inbound-by-date 마감정보입력+매입마감 연결
- 입고등록 master SELECT 함정 수정 — RPS 에 POM.delivery_status 없어 reception_status fallback
- DataGrid 다중 frozen 누적 left 계산 인프라 추가 (frozenLeftPx props 보강)
  — shadcn Table 기반이라 진짜 column pinning 불가 (자연 위치 도달 후 sticky),
    입고 3페이지의 frozen 부여는 일단 제거. 진짜 pinning 은 별도 작업
2026-05-20 10:04:39 +09:00

276 lines
9.0 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 interface InboundFormData {
master: {
pom_objid: string;
purchase_order_no: string;
project_no: string;
contract_mgmt_objid: string;
partner_objid: string;
partner_name: string;
delivery_status: string;
};
parts: {
order_part_objid: string;
part_objid: string;
part_no: string;
part_name: string;
spec: string;
maker: string;
unit_title: string;
order_qty: number;
arrival_qty: number;
non_arrival_qty: number;
delivery_request_date: string;
}[];
arrivals: {
objid: string;
order_part_objid: string;
part_objid: string;
group_seq: string;
seq: string;
receipt_date: string;
location: string;
sub_location: string;
receipt_qty: number;
arrival_qty: number;
inventory_status: string;
}[];
}
export interface InboundSaveRow {
objid?: string;
parent_objid: string;
order_part_objid: string;
part_objid: string;
group_seq: string;
seq: string;
receipt_date: string;
location: string;
sub_location: string;
receipt_qty: number;
arrival_qty: number;
}
export interface DeadlineInfoPayload {
objIds: string[];
taxType: string;
taxInvoiceDate?: string;
exportDeclNo?: string;
loadingDate?: string;
foreignType?: string;
duty?: string;
importVat?: string;
exchangeRate?: 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 getInboundForm(pomObjid: string): Promise<InboundFormData> {
const r = await apiClient.get(`/purchase/inbound-form/${encodeURIComponent(pomObjid)}`);
return r.data?.data as InboundFormData;
},
async saveInboundForm(pomObjid: string, rows: InboundSaveRow[]): Promise<{ saved: number }> {
const r = await apiClient.post("/purchase/inbound-form/save", { pomObjid, rows });
return r.data?.data as { saved: number };
},
async saveArrivalDeadline(body: DeadlineInfoPayload): Promise<{ updated: number }> {
const r = await apiClient.post("/purchase/arrival/deadline", body);
return r.data?.data as { updated: number };
},
async closeArrival(objIds: string[]): Promise<{ updated: number }> {
const r = await apiClient.post("/purchase/arrival/close", { objIds });
return r.data?.data as { updated: number };
},
async listWarehouses(): Promise<OptionItem[]> {
const r = await apiClient.get("/purchase/options/warehouses");
return (r.data?.data ?? []) as OptionItem[];
},
async listAcctCodes(): Promise<OptionItem[]> {
const r = await apiClient.get("/purchase/options/acct-codes");
return (r.data?.data ?? []) as OptionItem[];
},
// 공통 옵션
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;
}