fix(WEB): 수주 페이지 필드 매핑 및 제품-부품 트리 구조 개선

- ApiClient 인터페이스: representative → manager_name, contact_person 변경
- transformApiToFrontend: client.representative → client.manager_name 수정
- ApiOrderItem에 floor_code, symbol_code 필드 추가 (제품-부품 매핑)
- ApiOrder에 options 타입 정의 추가
- ApiQuote에 calculation_inputs 타입 정의 추가
- 수주 상세 페이지 제품-부품 트리 구조 UI 개선
This commit is contained in:
2026-01-16 21:59:06 +09:00
committed by hskwon
parent dac1d9bc2b
commit d12618f320
10 changed files with 2043 additions and 164 deletions

View File

@@ -100,10 +100,11 @@ export default function EstimateDetailForm({
console.log('🔍 [handleConfirmSave] formData.priceAdjustmentData:', formData.priceAdjustmentData);
console.log('🔍 [handleConfirmSave] formData 전체:', formData);
// 현재 사용자 이름을 견적자로 설정하여 저장
// 현재 사용자 이름을 견적자로 설정하고, 상태를 견적완료로 변경하여 저장
const result = await updateEstimate(estimateId, {
...formData,
estimatorName: currentUser!.name,
status: 'completed', // 저장 시 견적완료 상태로 변경 (입찰에서 조회 가능)
});
if (result.success) {

View File

@@ -30,6 +30,13 @@ interface ApiOrder {
memo: string | null;
remarks: string | null;
note: string | null;
options: {
shipping_cost_code?: string;
receiver?: string;
receiver_contact?: string;
shipping_address?: string;
shipping_address_detail?: string;
} | null;
created_by: number | null;
updated_by: number | null;
created_at: string;
@@ -45,6 +52,9 @@ interface ApiOrderItem {
item_id: number | null;
item_name: string;
specification: string | null;
// 제품-부품 매핑용 코드
floor_code: string | null;
symbol_code: string | null;
quantity: number;
unit: string | null;
unit_price: number;
@@ -58,7 +68,8 @@ interface ApiClient {
id: number;
name: string;
business_no?: string;
representative?: string;
contact_person?: string;
manager_name?: string;
phone?: string;
email?: string;
}
@@ -68,6 +79,17 @@ interface ApiQuote {
quote_no: string;
quote_number?: string;
site_name: string | null;
calculation_inputs?: {
items?: Array<{
productCategory?: string;
productName?: string;
openWidth?: string;
openHeight?: string;
quantity?: number;
floor?: string;
code?: string;
}>;
} | null;
}
// 견적 목록 조회용 상세 타입
@@ -215,6 +237,16 @@ export interface Order {
subtotal?: number; // 소계 (supply_amount와 동일)
discountRate?: number; // 할인율
totalAmount?: number; // 총금액 (amount와 동일하지만 명시적)
// 제품 정보 (견적의 calculation_inputs에서 가져옴)
products?: Array<{
productName: string;
productCategory?: string;
openWidth?: string;
openHeight?: string;
quantity: number;
floor?: string;
code?: string;
}>;
}
export interface OrderItem {
@@ -411,17 +443,28 @@ function transformApiToFrontend(apiData: ApiOrder): Order {
remarks: apiData.remarks ?? undefined,
note: apiData.note ?? undefined,
items: apiData.items?.map(transformItemApiToFrontend) || [], // 상세 페이지용 추가 필드 (API에서 매핑)
manager: apiData.client?.representative ?? undefined,
manager: apiData.client?.manager_name ?? undefined,
contact: apiData.client_contact ?? apiData.client?.phone ?? undefined,
deliveryRequestDate: apiData.delivery_date ?? undefined, // delivery_date를 공유
shippingCost: undefined, // API에 해당 필드 없음 - 추후 구현
receiver: undefined, // API에 해당 필드 없음 - 추후 구현
receiverContact: undefined, // API에 해당 필드 없음 - 추후 구현
address: undefined, // API에 해당 필드 없음 - 추후 구현
addressDetail: undefined, // API에 해당 필드 없음 - 추후 구현
// options JSON에서 추출
shippingCost: apiData.options?.shipping_cost_code ?? undefined,
receiver: apiData.options?.receiver ?? undefined,
receiverContact: apiData.options?.receiver_contact ?? undefined,
address: apiData.options?.shipping_address ?? undefined,
addressDetail: apiData.options?.shipping_address_detail ?? undefined,
subtotal: apiData.supply_amount,
discountRate: apiData.discount_rate,
totalAmount: apiData.total_amount,
// 제품 정보 (견적의 calculation_inputs에서 추출)
products: apiData.quote?.calculation_inputs?.items?.map(item => ({
productName: item.productName || '',
productCategory: item.productCategory,
openWidth: item.openWidth,
openHeight: item.openHeight,
quantity: item.quantity || 1,
floor: item.floor,
code: item.code,
})) || [],
};
}
@@ -433,8 +476,8 @@ function transformItemApiToFrontend(apiItem: ApiOrderItem): OrderItem {
itemName: apiItem.item_name,
specification: apiItem.specification ?? undefined,
spec: apiItem.specification ?? undefined, // specification alias
type: undefined, // 층 - API에 해당 필드 없음
symbol: undefined, // 부호 - API에 해당 필드 없음
type: apiItem.floor_code ?? undefined, // 층 코드 (제품-부품 매핑용)
symbol: apiItem.symbol_code ?? undefined, // 부호 코드 (제품-부품 매핑용)
quantity: apiItem.quantity,
unit: apiItem.unit ?? undefined,
unitPrice: apiItem.unit_price,
@@ -461,11 +504,13 @@ function transformFrontendToApi(data: OrderFormData | Record<string, unknown>):
const selectedQuotation = formData.selectedQuotation as { id?: string } | undefined;
const quoteIdValue = selectedQuotation?.id ? parseInt(selectedQuotation.id, 10) || null : null;
return {
// Build result object - only include client_id if explicitly provided
// to avoid overwriting existing value with null on update
const result: Record<string, unknown> = {
quote_id: quoteIdValue,
order_type_code: formData.orderTypeCode || 'ORDER',
category_code: formData.categoryCode || null,
client_id: clientIdValue,
// client_id is conditionally added below
client_name: formData.clientName || null,
client_contact: formData.clientContact || formData.contact || null,
site_name: formData.siteName || null,
@@ -480,6 +525,14 @@ function transformFrontendToApi(data: OrderFormData | Record<string, unknown>):
memo: formData.memo || null,
remarks: formData.remarks || null,
note: formData.note || null,
// options JSON으로 묶어서 저장 (운임비용, 수신자, 수신처 연락처, 주소)
options: {
shipping_cost_code: formData.shippingCost || null,
receiver: formData.receiver || null,
receiver_contact: formData.receiverContact || null,
shipping_address: formData.address || null,
shipping_address_detail: formData.addressDetail || null,
},
items: items.map((item) => {
// Handle both form's OrderItem (id, spec) and API's OrderItemFormData (itemId, specification)
// 중요: 문자열로 전달될 수 있으므로 반드시 Number()로 변환
@@ -502,6 +555,14 @@ function transformFrontendToApi(data: OrderFormData | Record<string, unknown>):
};
}),
};
// Only include client_id if explicitly provided (not undefined)
// This prevents overwriting existing client_id with null on update
if (clientId !== undefined) {
result.client_id = clientIdValue;
}
return result;
}
function transformWorkOrderApiToFrontend(apiData: ApiWorkOrder): WorkOrder {
@@ -1022,6 +1083,117 @@ export async function createProductionOrder(
}
}
/**
* 생산지시 되돌리기 (작업지시 및 관련 데이터 삭제)
*/
export async function revertProductionOrder(orderId: string): Promise<{
success: boolean;
data?: {
order: Order;
deletedCounts: {
workResults: number;
workOrderItems: number;
workOrders: number;
};
previousStatus: string;
};
error?: string;
__authError?: boolean;
}> {
try {
const { response, error } = await serverFetch(
`${process.env.NEXT_PUBLIC_API_URL}/api/v1/orders/${orderId}/revert-production`,
{ method: 'POST' }
);
if (error) {
return { success: false, error: error.message, __authError: error.code === 'UNAUTHORIZED' };
}
if (!response) {
return { success: false, error: '생산지시 되돌리기에 실패했습니다.' };
}
const result: ApiResponse<{
order: ApiOrder;
deleted_counts: {
work_results: number;
work_order_items: number;
work_orders: number;
};
previous_status: string;
}> = await response.json();
if (!response.ok || !result.success) {
return { success: false, error: result.message || '생산지시 되돌리기에 실패했습니다.' };
}
return {
success: true,
data: {
order: transformApiToFrontend(result.data.order),
deletedCounts: {
workResults: result.data.deleted_counts.work_results,
workOrderItems: result.data.deleted_counts.work_order_items,
workOrders: result.data.deleted_counts.work_orders,
},
previousStatus: result.data.previous_status,
},
};
} catch (error) {
console.error('[revertProductionOrder] Error:', error);
return { success: false, error: '서버 오류가 발생했습니다.' };
}
}
/**
* 수주확정 되돌리기 (수주등록 상태로 변경)
*/
export async function revertOrderConfirmation(orderId: string): Promise<{
success: boolean;
data?: {
order: Order;
previousStatus: string;
};
error?: string;
__authError?: boolean;
}> {
try {
const { response, error } = await serverFetch(
`${process.env.NEXT_PUBLIC_API_URL}/api/v1/orders/${orderId}/revert-confirmation`,
{ method: 'POST' }
);
if (error) {
return { success: false, error: error.message, __authError: error.code === 'UNAUTHORIZED' };
}
if (!response) {
return { success: false, error: '수주확정 되돌리기에 실패했습니다.' };
}
const result: ApiResponse<{
order: ApiOrder;
previous_status: string;
}> = await response.json();
if (!response.ok || !result.success) {
return { success: false, error: result.message || '수주확정 되돌리기에 실패했습니다.' };
}
return {
success: true,
data: {
order: transformApiToFrontend(result.data.order),
previousStatus: result.data.previous_status,
},
};
} catch (error) {
console.error('[revertOrderConfirmation] Error:', error);
return { success: false, error: '서버 오류가 발생했습니다.' };
}
}
/**
* 수주 변환용 확정 견적 목록 조회
* QuotationSelectDialog에서 사용

View File

@@ -3,28 +3,40 @@
/**
* 계약서 문서 컴포넌트
* - 스크린샷 형식 + 지출결의서 디자인 스타일
* - 제품 정보는 견적의 calculation_inputs에서 추출한 products로 표시
*/
import { formatAmount } from "@/utils/formatAmount";
import { OrderItem } from "@/components/orders";
import { OrderItem } from "../actions";
// 제품 정보 타입
interface ProductInfo {
productName: string;
productCategory?: string;
openWidth?: string;
openHeight?: string;
quantity: number;
floor?: string;
code?: string;
}
interface ContractDocumentProps {
orderNumber: string;
orderDate: string;
client: string;
clientBusinessNumber?: string;
clientCeo?: string;
siteName?: string;
clientManager?: string;
clientContact?: string;
clientAddress?: string;
companyName?: string;
companyCeo?: string;
companyBusinessNumber?: string;
companyContact?: string;
companyAddress?: string;
items: OrderItem[];
subtotal: number;
discountRate: number;
totalAmount: number;
items?: OrderItem[];
products?: ProductInfo[];
subtotal?: number;
discountRate?: number;
totalAmount?: number;
remarks?: string;
}
@@ -32,19 +44,19 @@ export function ContractDocument({
orderNumber,
orderDate,
client,
clientBusinessNumber = "123-45-67890",
clientCeo = "대표자",
clientContact = "02-1234-5678",
clientAddress = "서울시 강남구",
companyName = "(주)케이디산업",
companyCeo = "김대표",
companyBusinessNumber = "111-22-33333",
companyContact = "02-9999-8888",
companyAddress = "경기도 화성시 케이디로 123",
items,
subtotal,
discountRate,
totalAmount,
siteName,
clientManager,
clientContact,
companyName,
companyCeo,
companyBusinessNumber,
companyContact,
companyAddress,
items = [],
products,
subtotal = 0,
discountRate = 0,
totalAmount = 0,
remarks,
}: ContractDocumentProps) {
const discountAmount = Math.round(subtotal * (discountRate / 100));
@@ -62,51 +74,63 @@ export function ContractDocument({
</p>
</div>
{/* 제품 */}
{/* 제품 정보 (개소별) */}
<div className="border border-gray-300 mb-4">
<div className="bg-gray-800 text-white p-2 text-sm font-medium text-center">
( )
</div>
<div className="p-3 text-center text-sm">
()
</div>
</div>
<div className="p-3">
{products && products.length > 0 ? (
<div className="space-y-3">
{products.map((product, index) => (
<div
key={index}
className="border border-gray-200 rounded p-3 bg-gray-50"
>
<div className="flex items-center justify-between mb-2">
<span className="text-xs font-medium text-gray-500 bg-white px-2 py-0.5 rounded border">
{index + 1}
</span>
{product.floor && (
<span className="text-xs font-medium text-blue-600 bg-blue-50 px-2 py-0.5 rounded">
{product.floor}
</span>
)}
</div>
{/* 수주물목 테이블 */}
<div className="border border-gray-300 mb-4">
<div className="bg-gray-800 text-white p-2 text-sm font-medium text-center">
( )
<div className="grid grid-cols-2 md:grid-cols-4 gap-3 text-sm">
<div>
<span className="text-gray-500 text-xs"></span>
<p className="font-medium">{product.productName}</p>
</div>
<div>
<span className="text-gray-500 text-xs"></span>
<p className="font-medium">
{product.openWidth && product.openHeight
? `${product.openWidth} × ${product.openHeight} mm`
: "-"}
</p>
</div>
<div>
<span className="text-gray-500 text-xs"></span>
<p className="font-medium">{product.quantity} SET</p>
</div>
{product.code && (
<div>
<span className="text-gray-500 text-xs"></span>
<p className="font-medium">{product.code}</p>
</div>
)}
</div>
</div>
))}
</div>
) : (
<p className="text-center text-gray-400 py-4">
</p>
)}
</div>
<table className="w-full text-sm">
<thead>
<tr className="bg-gray-100 border-b border-gray-300">
<th className="p-2 text-center font-medium border-r border-gray-300 w-24"></th>
<th className="p-2 text-left font-medium border-r border-gray-300"></th>
<th className="p-2 text-center font-medium border-r border-gray-300 w-28"></th>
<th className="p-2 text-center font-medium border-r border-gray-300 w-16"></th>
<th className="p-2 text-center font-medium w-16"></th>
</tr>
</thead>
<tbody>
{items.length > 0 ? (
items.map((item) => (
<tr key={item.id} className="border-b border-gray-300">
<td className="p-2 text-center border-r border-gray-300">{item.itemCode}</td>
<td className="p-2 border-r border-gray-300">{item.itemName}</td>
<td className="p-2 text-center border-r border-gray-300">{item.spec}</td>
<td className="p-2 text-center border-r border-gray-300">{item.quantity}</td>
<td className="p-2 text-center">{item.unit}</td>
</tr>
))
) : (
<tr className="border-b border-gray-300">
<td colSpan={5} className="p-4 text-center text-gray-400">
</td>
</tr>
)}
</tbody>
</table>
</div>
{/* 발주처/당사 정보 */}
@@ -118,19 +142,19 @@ export function ContractDocument({
<div className="p-3 space-y-1 text-sm">
<div className="flex">
<span className="w-20 text-gray-600"></span>
<span>{client}</span>
<span>{client || "-"}</span>
</div>
<div className="flex">
<span className="w-20 text-gray-600"></span>
<span>-</span>
<span>{siteName || "-"}</span>
</div>
<div className="flex">
<span className="w-20 text-gray-600"></span>
<span>{clientCeo}</span>
<span>{clientManager || "-"}</span>
</div>
<div className="flex">
<span className="w-20 text-gray-600"></span>
<span>{clientContact}</span>
<span>{clientContact || "-"}</span>
</div>
</div>
</div>
@@ -141,23 +165,23 @@ export function ContractDocument({
<div className="p-3 space-y-1 text-sm">
<div className="flex">
<span className="w-20 text-gray-600"></span>
<span>{companyName}</span>
<span>{companyName || "-"}</span>
</div>
<div className="flex">
<span className="w-20 text-gray-600"></span>
<span>{companyCeo}</span>
<span>{companyCeo || "-"}</span>
</div>
<div className="flex">
<span className="w-20 text-gray-600"></span>
<span>{companyBusinessNumber}</span>
<span>{companyBusinessNumber || "-"}</span>
</div>
<div className="flex">
<span className="w-20 text-gray-600"></span>
<span>{companyContact}</span>
<span>{companyContact || "-"}</span>
</div>
<div className="flex">
<span className="w-20 text-gray-600"></span>
<span>{companyAddress}</span>
<span>{companyAddress || "-"}</span>
</div>
</div>
</div>
@@ -178,21 +202,21 @@ export function ContractDocument({
<tbody>
<tr className="border-b border-gray-300">
<td className="p-2 bg-gray-100 border-r border-gray-300 w-32"></td>
<td className="p-2 text-right border-r border-gray-300">{formatAmount(subtotal)}</td>
<td className="p-2 text-right border-r border-gray-300">{formatAmount(subtotal)}</td>
<td className="p-2 bg-gray-100 border-r border-gray-300 w-32"></td>
<td className="p-2 text-right">{discountRate}%</td>
</tr>
<tr className="border-b border-gray-300">
<td className="p-2 bg-gray-100 border-r border-gray-300"></td>
<td className="p-2 text-right border-r border-gray-300 text-red-600">-{formatAmount(discountAmount)}</td>
<td className="p-2 text-right border-r border-gray-300 text-red-600">-{formatAmount(discountAmount)}</td>
<td className="p-2 bg-gray-100 border-r border-gray-300"> </td>
<td className="p-2 text-right">{formatAmount(afterDiscount)}</td>
<td className="p-2 text-right">{formatAmount(afterDiscount)}</td>
</tr>
<tr>
<td className="p-2 bg-gray-100 border-r border-gray-300">(10%)</td>
<td className="p-2 text-right border-r border-gray-300">{formatAmount(vat)}</td>
<td className="p-2 text-right border-r border-gray-300">{formatAmount(vat)}</td>
<td className="p-2 bg-gray-100 border-r border-gray-300 font-medium"></td>
<td className="p-2 text-right font-semibold">{formatAmount(finalTotal)}</td>
<td className="p-2 text-right font-semibold">{formatAmount(finalTotal)}</td>
</tr>
</tbody>
</table>

View File

@@ -5,6 +5,7 @@
* - 계약서, 거래명세서, 발주서를 모달 형태로 표시
*/
import { useState, useEffect } from "react";
import {
Dialog,
DialogContent,
@@ -24,27 +25,40 @@ import { ContractDocument } from "./ContractDocument";
import { TransactionDocument } from "./TransactionDocument";
import { PurchaseOrderDocument } from "./PurchaseOrderDocument";
import { printArea } from "@/lib/print-utils";
import { OrderItem } from "../ItemAddDialog";
import { OrderItem } from "../actions";
import { getCompanyInfo } from "@/components/settings/CompanyInfoManagement/actions";
// 문서 타입
export type OrderDocumentType = "contract" | "transaction" | "purchaseOrder";
// 제품 정보 타입 (견적의 calculation_inputs에서 추출)
export interface ProductInfo {
productName: string;
productCategory?: string;
openWidth?: string;
openHeight?: string;
quantity: number;
floor?: string;
code?: string;
}
// 문서 데이터 인터페이스
export interface OrderDocumentData {
lotNumber: string;
orderDate: string;
client: string;
siteName: string;
manager: string;
managerContact: string;
deliveryRequestDate: string;
expectedShipDate: string;
deliveryMethod: string;
address: string;
items: OrderItem[];
subtotal: number;
discountRate: number;
totalAmount: number;
siteName?: string;
manager?: string;
managerContact?: string;
deliveryRequestDate?: string;
expectedShipDate?: string;
deliveryMethod?: string;
address?: string;
items?: OrderItem[];
products?: ProductInfo[];
subtotal?: number;
discountRate?: number;
totalAmount?: number;
remarks?: string;
}
@@ -55,12 +69,40 @@ interface OrderDocumentModalProps {
data: OrderDocumentData;
}
// 회사 정보 타입
interface CompanyInfo {
companyName: string;
representativeName: string;
businessNumber: string;
managerPhone: string;
address: string;
}
export function OrderDocumentModal({
open,
onOpenChange,
documentType,
data,
}: OrderDocumentModalProps) {
const [companyInfo, setCompanyInfo] = useState<CompanyInfo | null>(null);
// 모달이 열릴 때 회사 정보 조회
useEffect(() => {
if (open && !companyInfo) {
getCompanyInfo().then((result) => {
if (result.success && result.data) {
setCompanyInfo({
companyName: result.data.companyName,
representativeName: result.data.representativeName,
businessNumber: result.data.businessNumber,
managerPhone: result.data.managerPhone,
address: result.data.address,
});
}
});
}
}, [open, companyInfo]);
const getDocumentTitle = () => {
switch (documentType) {
case "contract":
@@ -102,10 +144,19 @@ export function OrderDocumentModal({
orderNumber={data.lotNumber}
orderDate={data.orderDate}
client={data.client}
items={data.items}
subtotal={data.subtotal}
discountRate={data.discountRate}
totalAmount={data.totalAmount}
siteName={data.siteName}
clientManager={data.manager}
clientContact={data.managerContact}
companyName={companyInfo?.companyName}
companyCeo={companyInfo?.representativeName}
companyBusinessNumber={companyInfo?.businessNumber}
companyContact={companyInfo?.managerPhone}
companyAddress={companyInfo?.address}
items={data.items || []}
products={data.products}
subtotal={data.subtotal || 0}
discountRate={data.discountRate || 0}
totalAmount={data.totalAmount || 0}
remarks={data.remarks}
/>
);
@@ -115,10 +166,10 @@ export function OrderDocumentModal({
orderNumber={data.lotNumber}
orderDate={data.orderDate}
client={data.client}
items={data.items}
subtotal={data.subtotal}
discountRate={data.discountRate}
totalAmount={data.totalAmount}
items={data.items || []}
subtotal={data.subtotal || 0}
discountRate={data.discountRate || 0}
totalAmount={data.totalAmount || 0}
/>
);
case "purchaseOrder":
@@ -126,14 +177,14 @@ export function OrderDocumentModal({
<PurchaseOrderDocument
orderNumber={data.lotNumber}
client={data.client}
siteName={data.siteName}
siteName={data.siteName || "-"}
manager={data.manager}
managerContact={data.managerContact}
deliveryRequestDate={data.deliveryRequestDate}
deliveryRequestDate={data.deliveryRequestDate || "-"}
expectedShipDate={data.expectedShipDate}
deliveryMethod={data.deliveryMethod}
address={data.address}
items={data.items}
address={data.address || "-"}
items={data.items || []}
remarks={data.remarks}
/>
);

View File

@@ -5,37 +5,37 @@
* - 스크린샷 형식 + 지출결의서 디자인 스타일
*/
import { OrderItem } from "@/components/orders";
import { OrderItem } from "../actions";
interface PurchaseOrderDocumentProps {
orderNumber: string;
client: string;
siteName: string;
siteName?: string;
manager?: string;
managerContact?: string;
deliveryRequestDate: string;
deliveryRequestDate?: string;
expectedShipDate?: string;
deliveryMethod?: string;
address: string;
address?: string;
orderDate?: string;
installationCount?: number;
items: OrderItem[];
items?: OrderItem[];
remarks?: string;
}
export function PurchaseOrderDocument({
orderNumber,
client,
siteName,
siteName = "-",
manager = "-",
managerContact = "010-0123-4567",
deliveryRequestDate,
deliveryRequestDate = "-",
expectedShipDate = "-",
deliveryMethod = "상차",
address,
address = "-",
orderDate = new Date().toISOString().split("T")[0],
installationCount = 3,
items,
items = [],
remarks,
}: PurchaseOrderDocumentProps) {
return (

View File

@@ -6,7 +6,7 @@
*/
import { formatAmount } from "@/utils/formatAmount";
import { OrderItem } from "@/components/orders";
import { OrderItem } from "../actions";
interface TransactionDocumentProps {
orderNumber: string;
@@ -22,10 +22,10 @@ interface TransactionDocumentProps {
companyBusinessNumber?: string;
companyContact?: string;
companyAddress?: string;
items: OrderItem[];
subtotal: number;
discountRate: number;
totalAmount: number;
items?: OrderItem[];
subtotal?: number;
discountRate?: number;
totalAmount?: number;
}
export function TransactionDocument({
@@ -42,10 +42,10 @@ export function TransactionDocument({
companyBusinessNumber = "123-45-67890",
companyContact = "02-1234-5678",
companyAddress = "서울 강남구 테헤란로 123",
items,
subtotal,
discountRate,
totalAmount,
items = [],
subtotal = 0,
discountRate = 0,
totalAmount = 0,
}: TransactionDocumentProps) {
const discountAmount = Math.round(subtotal * (discountRate / 100));
const afterDiscount = subtotal - discountAmount;

View File

@@ -12,6 +12,8 @@ export {
deleteOrders,
updateOrderStatus,
getOrderStats,
revertProductionOrder,
revertOrderConfirmation,
type Order,
type OrderItem as OrderItemApi,
type OrderFormData as OrderApiFormData,