feat(P18B-3,P18B-4): 实现资产盘点页面与流转前端入口
这个提交包含在:
+41
-3
@@ -106,7 +106,7 @@ export type AssetListQuery = {
|
||||
mine?: boolean;
|
||||
};
|
||||
|
||||
export type AssetPayload = {
|
||||
export type CreateAssetPayload = {
|
||||
name: string;
|
||||
categoryId: string;
|
||||
legalEntityId: string;
|
||||
@@ -120,6 +120,20 @@ export type AssetPayload = {
|
||||
retiredAt?: string | null;
|
||||
};
|
||||
|
||||
export type PatchAssetPayload = Partial<
|
||||
Omit<CreateAssetPayload, "purchaseRequestId" | "assigneeEmployeeId">
|
||||
>;
|
||||
|
||||
export type AssetMovementResponse = {
|
||||
assetId: string;
|
||||
assetNo: string;
|
||||
status: AssetStatus;
|
||||
assigneeEmployeeId: string | null;
|
||||
location: string | null;
|
||||
assignmentId?: string | null;
|
||||
transferId?: string | null;
|
||||
};
|
||||
|
||||
function toQueryString(query: Record<string, string | boolean | undefined>) {
|
||||
const params = new URLSearchParams();
|
||||
for (const [key, value] of Object.entries(query)) {
|
||||
@@ -181,16 +195,40 @@ export async function fetchAssetDetail(assetId: string) {
|
||||
return apiRequestData<{ asset: AssetItem }>(`/api/assets/${assetId}`);
|
||||
}
|
||||
|
||||
export async function createAsset(payload: AssetPayload) {
|
||||
export async function createAsset(payload: CreateAssetPayload) {
|
||||
return apiRequestData<{ asset: AssetItem }>("/api/assets", {
|
||||
method: "POST",
|
||||
body: JSON.stringify(payload),
|
||||
});
|
||||
}
|
||||
|
||||
export async function patchAsset(assetId: string, payload: Partial<Omit<AssetPayload, "purchaseRequestId">>) {
|
||||
export async function patchAsset(assetId: string, payload: PatchAssetPayload) {
|
||||
return apiRequestData<{ asset: AssetItem }>(`/api/assets/${assetId}`, {
|
||||
method: "PATCH",
|
||||
body: JSON.stringify(payload),
|
||||
});
|
||||
}
|
||||
|
||||
export async function assignAsset(assetId: string, payload: { employeeId: string; note?: string | null }) {
|
||||
return apiRequestData<AssetMovementResponse>(`/api/assets/${assetId}/assign`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify(payload),
|
||||
});
|
||||
}
|
||||
|
||||
export async function returnAsset(assetId: string, payload: { note?: string | null } = {}) {
|
||||
return apiRequestData<AssetMovementResponse>(`/api/assets/${assetId}/return`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify(payload),
|
||||
});
|
||||
}
|
||||
|
||||
export async function transferAsset(
|
||||
assetId: string,
|
||||
payload: { toEmployeeId?: string | null; toLocation?: string | null; note?: string | null },
|
||||
) {
|
||||
return apiRequestData<AssetMovementResponse>(`/api/assets/${assetId}/transfer`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify(payload),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
import { apiRequestData } from "./http";
|
||||
import type { AssetStatus } from "./assets";
|
||||
|
||||
export type InventoryCheckScopeType = "all" | "legal_entity" | "cost_center" | "location";
|
||||
export type InventoryCheckStatus = "draft" | "counting" | "completed";
|
||||
export type InventoryCheckResult = "matched" | "missing" | "extra" | "damaged";
|
||||
|
||||
export type InventoryCheckItem = {
|
||||
id: string;
|
||||
assetId: string;
|
||||
result: InventoryCheckResult;
|
||||
note: string | null;
|
||||
asset: {
|
||||
id: string;
|
||||
assetNo: string;
|
||||
name: string;
|
||||
status: AssetStatus;
|
||||
legalEntityId: string;
|
||||
costCenterId: string;
|
||||
location: string | null;
|
||||
assigneeEmployeeId: string | null;
|
||||
} | null;
|
||||
};
|
||||
|
||||
export type InventoryCheck = {
|
||||
id: string;
|
||||
checkNo: string;
|
||||
scopeType: InventoryCheckScopeType;
|
||||
scopeRefId: string | null;
|
||||
status: InventoryCheckStatus;
|
||||
startedAt: string | null;
|
||||
completedAt: string | null;
|
||||
createdAt: string | null;
|
||||
updatedAt: string | null;
|
||||
items: InventoryCheckItem[];
|
||||
};
|
||||
|
||||
function toQueryString(query: Record<string, string | undefined>) {
|
||||
const params = new URLSearchParams();
|
||||
for (const [key, value] of Object.entries(query)) {
|
||||
if (!value) {
|
||||
continue;
|
||||
}
|
||||
params.set(key, value);
|
||||
}
|
||||
const encoded = params.toString();
|
||||
return encoded ? `?${encoded}` : "";
|
||||
}
|
||||
|
||||
export async function fetchInventoryChecks(params: { status?: InventoryCheckStatus; scopeType?: InventoryCheckScopeType } = {}) {
|
||||
return apiRequestData<{ items: InventoryCheck[] }>(
|
||||
`/api/inventory-checks${toQueryString(params as Record<string, string | undefined>)}`,
|
||||
);
|
||||
}
|
||||
|
||||
export async function createInventoryCheck(payload: { scopeType: InventoryCheckScopeType; scopeRefId?: string | null }) {
|
||||
return apiRequestData<{ inventoryCheck: InventoryCheck }>("/api/inventory-checks", {
|
||||
method: "POST",
|
||||
body: JSON.stringify(payload),
|
||||
});
|
||||
}
|
||||
|
||||
export async function startInventoryCheck(inventoryCheckId: string) {
|
||||
return apiRequestData<{ inventoryCheck: InventoryCheck }>(`/api/inventory-checks/${inventoryCheckId}/start`, {
|
||||
method: "POST",
|
||||
});
|
||||
}
|
||||
|
||||
export async function recordInventoryItems(
|
||||
inventoryCheckId: string,
|
||||
payload: { items: Array<{ assetId: string; result: InventoryCheckResult; note?: string | null }> },
|
||||
) {
|
||||
return apiRequestData<{ inventoryCheck: InventoryCheck }>(`/api/inventory-checks/${inventoryCheckId}/items`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify(payload),
|
||||
});
|
||||
}
|
||||
|
||||
export async function completeInventoryCheck(inventoryCheckId: string) {
|
||||
return apiRequestData<{ inventoryCheck: InventoryCheck }>(`/api/inventory-checks/${inventoryCheckId}/complete`, {
|
||||
method: "POST",
|
||||
});
|
||||
}
|
||||
在新工单中引用
屏蔽一个用户