Parcourir la source

replenishment update

production
kelvin.yau il y a 1 semaine
Parent
révision
96c8ac643b
5 fichiers modifiés avec 1127 ajouts et 623 suppressions
  1. +6
    -0
      src/app/api/do/actions.tsx
  2. +859
    -540
      src/components/DoSearch/DoReplenishmentTab.tsx
  3. +206
    -59
      src/components/DoSearch/ReplenishmentFilterField.tsx
  4. +28
    -12
      src/i18n/en/do.json
  5. +28
    -12
      src/i18n/zh/do.json

+ 6
- 0
src/app/api/do/actions.tsx Voir le fichier

@@ -38,6 +38,8 @@ export interface DoDetailLine {
id: number; id: number;
itemNo: string; itemNo: string;
qty: number; qty: number;
/** Sum of stock_out_line qty for linked pick order line; falls back to qty. */
actualShippedQty?: number;
price: number; price: number;
status: string; status: string;
itemName?: string; itemName?: string;
@@ -680,6 +682,7 @@ export interface SubmitDoReplenishmentLineRequest {
sourceDoLineId: number; sourceDoLineId: number;
replenishQty: number; replenishQty: number;
truckLaneCode?: string; truckLaneCode?: string;
reason?: string;
} }


export interface DoReplenishmentRecord { export interface DoReplenishmentRecord {
@@ -692,6 +695,7 @@ export interface DoReplenishmentRecord {
itemId: number; itemId: number;
itemNo?: string; itemNo?: string;
itemName?: string; itemName?: string;
originalQty?: number;
replenishQty: number; replenishQty: number;
shortUom?: string; shortUom?: string;
shopCode?: string; shopCode?: string;
@@ -699,8 +703,10 @@ export interface DoReplenishmentRecord {
truckLaneCode?: string; truckLaneCode?: string;
targetDoId?: number; targetDoId?: number;
targetDoCode?: string; targetDoCode?: string;
targetDoEstimatedArrivalDate?: string;
pickOrderLineId?: number; pickOrderLineId?: number;
status: string; status: string;
reason?: string;
created?: string; created?: string;
} }




+ 859
- 540
src/components/DoSearch/DoReplenishmentTab.tsx
Fichier diff supprimé car celui-ci est trop grand
Voir le fichier


+ 206
- 59
src/components/DoSearch/ReplenishmentFilterField.tsx Voir le fichier

@@ -11,7 +11,7 @@ export const REPLENISHMENT_FIELD_LABEL_SX = (theme: Theme) => ({
theme.palette.mode === "dark" theme.palette.mode === "dark"
? theme.palette.grey[100] ? theme.palette.grey[100]
: theme.palette.common.black, : theme.palette.common.black,
fontWeight: 600,
fontWeight: 700,
}); });


export const REPLENISHMENT_FIELD_ICON_SX = (theme: Theme) => ({ export const REPLENISHMENT_FIELD_ICON_SX = (theme: Theme) => ({
@@ -28,6 +28,7 @@ export const REPLENISHMENT_TEXTFIELD_SX = (theme: Theme) =>
borderRadius: 2, borderRadius: 2,
bgcolor: theme.palette.mode === "dark" ? "grey.800" : "common.white", bgcolor: theme.palette.mode === "dark" ? "grey.800" : "common.white",
border: `1px solid ${theme.palette.divider}`, border: `1px solid ${theme.palette.divider}`,
...REPLENISHMENT_FIELD_CONTROL_ROOT_SX,
}, },
"& .MuiFilledInput-root.Mui-focused": { "& .MuiFilledInput-root.Mui-focused": {
bgcolor: theme.palette.mode === "dark" ? "grey.800" : "common.white", bgcolor: theme.palette.mode === "dark" ? "grey.800" : "common.white",
@@ -56,6 +57,7 @@ export const REPLENISHMENT_AUTOCOMPLETE_SX = (theme: Theme) =>
width: "100%", width: "100%",
}, },
"& .MuiAutocomplete-inputRoot": { "& .MuiAutocomplete-inputRoot": {
...REPLENISHMENT_FIELD_CONTROL_ROOT_SX,
paddingTop: `${REPLENISHMENT_FIELD_BODY_PY} !important`, paddingTop: `${REPLENISHMENT_FIELD_BODY_PY} !important`,
paddingBottom: `${REPLENISHMENT_FIELD_BODY_PY} !important`, paddingBottom: `${REPLENISHMENT_FIELD_BODY_PY} !important`,
paddingLeft: `${theme.spacing(REPLENISHMENT_FIELD_BODY_PX)} !important`, paddingLeft: `${theme.spacing(REPLENISHMENT_FIELD_BODY_PX)} !important`,
@@ -75,6 +77,75 @@ export const REPLENISHMENT_FIELD_BODY_PY = "12px";
/** Horizontal padding aligned with MUI filled input (spacing 1.5 = 12px). */ /** Horizontal padding aligned with MUI filled input (spacing 1.5 = 12px). */
export const REPLENISHMENT_FIELD_BODY_PX = 1.5; export const REPLENISHMENT_FIELD_BODY_PX = 1.5;


/** Fixed height for replenishment inputs, selects, and read-only value boxes. */
export const REPLENISHMENT_FIELD_CONTROL_HEIGHT = 44;

export const REPLENISHMENT_FIELD_CONTROL_ROOT_SX = {
height: REPLENISHMENT_FIELD_CONTROL_HEIGHT,
minHeight: REPLENISHMENT_FIELD_CONTROL_HEIGHT,
maxHeight: REPLENISHMENT_FIELD_CONTROL_HEIGHT,
boxSizing: "border-box" as const,
};

/** Read-only value box — same outer height as {@link ReplenishmentTextField}. */
export const REPLENISHMENT_READONLY_VALUE_SX = (theme: Theme) =>
({
...REPLENISHMENT_FIELD_CONTROL_ROOT_SX,
borderRadius: 2,
border: `1px solid ${theme.palette.divider}`,
bgcolor: theme.palette.mode === "dark" ? "grey.800" : "common.white",
px: REPLENISHMENT_FIELD_BODY_PX,
display: "flex",
alignItems: "center",
minWidth: 0,
overflow: "hidden",
}) as const;

export function ReplenishmentReadonlyValue({
children,
fontWeight,
}: {
children: React.ReactNode;
fontWeight?: number;
}) {
return (
<Box sx={REPLENISHMENT_READONLY_VALUE_SX}>
<Typography
variant="body2"
component="div"
fontWeight={fontWeight}
sx={{
overflow: "hidden",
textOverflow: "ellipsis",
whiteSpace: "nowrap",
width: "100%",
minWidth: 0,
lineHeight: 1.43,
}}
>
{children ?? "\u00A0"}
</Typography>
</Box>
);
}

/** Invisible label spacer so action buttons align with labelled fields. */
export function ReplenishmentFieldLabelSpacer() {
return (
<Typography
variant="body2"
aria-hidden
sx={{
visibility: "hidden",
lineHeight: 1.35,
userSelect: "none",
}}
>
{"\u00A0"}
</Typography>
);
}

/** Source DO summary header: same inset as textbox content area. */ /** Source DO summary header: same inset as textbox content area. */
export const REPLENISHMENT_SOURCE_HEADER_SX = (theme: Theme) => export const REPLENISHMENT_SOURCE_HEADER_SX = (theme: Theme) =>
({ ({
@@ -89,7 +160,7 @@ export const REPLENISHMENT_SOURCE_HEADER_SX = (theme: Theme) =>
}) as const; }) as const;


type ReplenishmentFieldLabelProps = { type ReplenishmentFieldLabelProps = {
icon: ReactNode;
icon?: ReactNode;
title: string; title: string;
required?: boolean; required?: boolean;
sx?: SxProps<Theme>; sx?: SxProps<Theme>;
@@ -102,14 +173,22 @@ export function ReplenishmentFieldLabel({
sx, sx,
}: ReplenishmentFieldLabelProps) { }: ReplenishmentFieldLabelProps) {
return ( return (
<Stack direction="row" spacing={1} alignItems="center" sx={sx}>
{icon}
<Typography variant="body2" sx={REPLENISHMENT_FIELD_LABEL_SX} component="span">
<Stack direction="row" spacing={icon ? 1 : 0} alignItems="center" sx={sx}>
{icon ?? null}
<Typography
variant="body2"
sx={(theme) => ({
...REPLENISHMENT_FIELD_LABEL_SX(theme),
whiteSpace: "normal",
lineHeight: 1.35,
})}
component="span"
>
{title} {title}
{required ? ( {required ? (
<Typography component="span" color="error.main" aria-hidden="true">
<Box component="span" color="error.main" aria-hidden="true">
{" *"} {" *"}
</Typography>
</Box>
) : null} ) : null}
</Typography> </Typography>
</Stack> </Stack>
@@ -163,10 +242,13 @@ export const REPLENISHMENT_FILLED_SELECT_SX = (theme: Theme) =>
bgcolor: theme.palette.mode === "dark" ? "grey.800" : "common.white", bgcolor: theme.palette.mode === "dark" ? "grey.800" : "common.white",
border: `1px solid ${theme.palette.divider}`, border: `1px solid ${theme.palette.divider}`,
"&::before, &::after": { display: "none" }, "&::before, &::after": { display: "none" },
...REPLENISHMENT_FIELD_CONTROL_ROOT_SX,
}, },
"& .MuiSelect-select": { "& .MuiSelect-select": {
paddingTop: REPLENISHMENT_FIELD_BODY_PY, paddingTop: REPLENISHMENT_FIELD_BODY_PY,
paddingBottom: REPLENISHMENT_FIELD_BODY_PY, paddingBottom: REPLENISHMENT_FIELD_BODY_PY,
display: "flex",
alignItems: "center",
}, },
}) as const; }) as const;


@@ -242,17 +324,57 @@ export function ReplenishmentQtyWithUomField({
); );
} }


/** Tracking dialog table — horizontal scroll, no fixed layout (avoids column text stacking). */
export const REPLENISHMENT_TRACKING_TABLE_SX = {
width: "max-content",
minWidth: "100%",
"& .MuiTableCell-root": {
typography: "body2",
borderColor: "divider",
py: 1,
px: 1.25,
whiteSpace: "nowrap",
},
"& .MuiTableCell-root:first-of-type": {
pl: 1.5,
},
"& .MuiTableHead-root .MuiTableCell-root": {
fontWeight: 600,
color: "text.secondary",
bgcolor: "action.hover",
borderBottom: "1px solid",
borderColor: "divider",
},
"& .MuiTableBody-root .MuiTableRow-root:not(:last-of-type) .MuiTableCell-root": {
borderBottom: "1px solid",
borderColor: "divider",
},
} as const;

export const REPLENISHMENT_TRACKING_CELL_ELLIPSIS_SX = {
maxWidth: 160,
overflow: "hidden",
textOverflow: "ellipsis",
} as const;

export const REPLENISHMENT_TRACKING_CELL_WRAP_SX = {
minWidth: 120,
maxWidth: 200,
whiteSpace: "normal",
wordBreak: "break-word",
} as const;

export const REPLENISHMENT_TABLE_SX = { export const REPLENISHMENT_TABLE_SX = {
tableLayout: { md: "fixed" },
width: "100%", width: "100%",
tableLayout: "fixed",
"& .MuiTableCell-root": { "& .MuiTableCell-root": {
typography: "body2", typography: "body2",
borderColor: "divider", borderColor: "divider",
py: 1.25,
px: 2,
py: 1,
px: 1.25,
}, },
"& .MuiTableCell-root:first-of-type": { "& .MuiTableCell-root:first-of-type": {
pl: 3.5,
pl: 1.5,
}, },
"& .MuiTableHead-root .MuiTableCell-root": { "& .MuiTableHead-root .MuiTableCell-root": {
fontWeight: 600, fontWeight: 600,
@@ -378,10 +500,27 @@ export const REPLENISHMENT_TABLE_ACTION_CELL_INNER_SX = {
width: "100%", width: "100%",
} as const; } as const;


/** In-table select — compact padding; truncate long selected labels. */
export const REPLENISHMENT_TABLE_INLINE_SELECT_SX = (theme: Theme) =>
({
...REPLENISHMENT_FILLED_SELECT_SX(theme),
"& .MuiSelect-select": {
paddingTop: "6px",
paddingBottom: "6px",
paddingLeft: theme.spacing(1),
paddingRight: `${theme.spacing(3)} !important`,
overflow: "hidden",
textOverflow: "ellipsis",
whiteSpace: "nowrap",
},
}) as const;

export const replenishmentSearchGridLabelSx = (col: number) => ({ export const replenishmentSearchGridLabelSx = (col: number) => ({
gridColumn: { xs: 1, lg: col }, gridColumn: { xs: 1, lg: col },
gridRow: { xs: "auto", lg: 1 }, gridRow: { xs: "auto", lg: 1 },
minWidth: 0,
minWidth: "min-content",
overflow: "hidden",
textOverflow: "ellipsis",
}); });


export const replenishmentSearchGridInputSx = (col: number) => ({ export const replenishmentSearchGridInputSx = (col: number) => ({
@@ -395,66 +534,74 @@ export const replenishmentSearchGridInputSx = (col: number) => ({
}, },
}); });


/** Shop input + lookup button share one row; button height follows the textbox. */
export const replenishmentSearchGridShopRowSx = {
gridColumn: { xs: 1, lg: 3 },
/** Lookup / tracking buttons beside the three filter inputs (4th grid column on lg). */
export const replenishmentSearchGridActionsSx = {
gridColumn: { xs: 1, lg: 4 },
gridRow: { xs: "auto", lg: 2 }, gridRow: { xs: "auto", lg: 2 },
minWidth: 0,
display: "flex", display: "flex",
justifyContent: { xs: "stretch", lg: "flex-start" },
alignItems: "stretch", alignItems: "stretch",
gap: 1,
"& .MuiTextField-root": {
flex: 1,
minWidth: 0,
},
"& .MuiFormControl-root": {
height: "100%",
},
"& .MuiFilledInput-root": {
height: "100%",
boxSizing: "border-box",
flexWrap: { xs: "wrap", lg: "nowrap" },
gap: 1.5,
minWidth: 0,
"& .MuiButton-root": {
flex: { xs: 1, lg: "0 0 auto" },
alignSelf: "stretch",
}, },
}; };


/** Match {@link ReplenishmentFieldLabel} typography on contained buttons. */
/** Match {@link ReplenishmentFieldLabel} typography on field-height buttons. */
export const REPLENISHMENT_LOOKUP_BUTTON_TEXT_SX = (theme: Theme) => ({ export const REPLENISHMENT_LOOKUP_BUTTON_TEXT_SX = (theme: Theme) => ({
fontSize: theme.typography.body2.fontSize, fontSize: theme.typography.body2.fontSize,
fontWeight: 600, fontWeight: 600,
lineHeight: 1, lineHeight: 1,
}); });


export const REPLENISHMENT_LOOKUP_BUTTON_SX = (theme: Theme) => ({
...REPLENISHMENT_LOOKUP_BUTTON_TEXT_SX(theme),
alignSelf: "stretch",
minHeight: "unset",
height: "auto",
py: 0,
px: 1.5,
borderRadius: 2,
boxShadow: "none",
textTransform: "none",
whiteSpace: "nowrap",
flexShrink: 0,
minWidth: { xs: "auto", lg: 108 },
"& .MuiButton-startIcon": {
margin: 0,
marginRight: theme.spacing(0.75),
"& > *:nth-of-type(1)": {
fontSize: 20,
/** Base button style — same 44px height as {@link ReplenishmentTextField}. */
export const REPLENISHMENT_FIELD_BUTTON_SX = (theme: Theme) =>
({
...REPLENISHMENT_LOOKUP_BUTTON_TEXT_SX(theme),
...REPLENISHMENT_FIELD_CONTROL_ROOT_SX,
paddingTop: 0,
paddingBottom: 0,
px: REPLENISHMENT_FIELD_BODY_PX,
borderRadius: 2,
boxShadow: "none",
textTransform: "none",
whiteSpace: "nowrap",
flexShrink: 0,
"&.MuiButton-root": {
...REPLENISHMENT_FIELD_CONTROL_ROOT_SX,
}, },
},
});
"& .MuiButton-startIcon": {
margin: 0,
marginRight: theme.spacing(0.75),
"& > *:nth-of-type(1)": {
fontSize: 18,
},
},
}) as const;

export const REPLENISHMENT_LOOKUP_BUTTON_SX = (theme: Theme) =>
({
...REPLENISHMENT_FIELD_BUTTON_SX(theme),
alignSelf: "stretch",
px: REPLENISHMENT_FIELD_BODY_PX,
minWidth: { xs: "auto", lg: 108 },
}) as const;


/** Outlined companion button (e.g. replenishment tracking) beside lookup. */ /** Outlined companion button (e.g. replenishment tracking) beside lookup. */
export const REPLENISHMENT_OUTLINED_ACTION_BUTTON_SX = (theme: Theme) => ({
...REPLENISHMENT_LOOKUP_BUTTON_SX(theme),
minWidth: "auto",
borderColor: theme.palette.divider,
color: theme.palette.text.primary,
bgcolor: theme.palette.mode === "dark" ? "grey.800" : "common.white",
"&:hover": {
borderColor: theme.palette.primary.main,
bgcolor: theme.palette.mode === "dark" ? "grey.700" : "grey.50",
},
});
export const REPLENISHMENT_OUTLINED_ACTION_BUTTON_SX = (theme: Theme) =>
({
...REPLENISHMENT_FIELD_BUTTON_SX(theme),
minWidth: "auto",
px: REPLENISHMENT_FIELD_BODY_PX,
borderColor: theme.palette.divider,
color: theme.palette.text.primary,
bgcolor: theme.palette.mode === "dark" ? "grey.800" : "common.white",
"&:hover": {
borderColor: theme.palette.primary.main,
bgcolor: theme.palette.mode === "dark" ? "grey.700" : "grey.50",
},
}) as const;



+ 28
- 12
src/i18n/en/do.json Voir le fichier

@@ -73,27 +73,35 @@
"Delivery Date": "Delivery Date", "Delivery Date": "Delivery Date",
"Enter last 4 characters of DO code": "Enter last 4 characters of DO code", "Enter last 4 characters of DO code": "Enter last 4 characters of DO code",
"Shop code, or first characters of shop name": "Shop code (partial match), or first characters of shop name", "Shop code, or first characters of shop name": "Shop code (partial match), or first characters of shop name",
"Multiple source DOs matched": "Multiple source DOs matched",
"Multiple source DOs matched": "Multiple original DOs matched",
"Please verify DO code suffix, delivery date and shop.": "Please verify DO code suffix, delivery date and shop.", "Please verify DO code suffix, delivery date and shop.": "Please verify DO code suffix, delivery date and shop.",
"Shop code or name is required": "Shop code or name is required", "Shop code or name is required": "Shop code or name is required",
"Draft List": "Draft List", "Draft List": "Draft List",
"Replenishment preview hint": "Add items from different source DOs, then batch submit from here.",
"Replenishment preview empty": "Added items appear here. Look up another source DO to keep adding.",
"Replenishment preview hint": "Add items from different original DOs, then batch submit from here.",
"Replenishment preview empty": "Added items appear here. Match another original DO to keep adding.",
"replenishmentCurrentDoDraftHint": "Added to draft list ({{count}} for this DO)",
"replenishmentTargetDoEstimatedArrivalDate": "Target DO Estimated Arrival Date",
"replenishmentOriginalSourceDoCode": "Original DO Code",
"replenishmentTargetDoCode": "Target DO Code",
"replenishmentStatusLabel": "Replenishment Status",
"replenishmentItemInfo": "Item Information",
"Clear": "Clear", "Clear": "Clear",
"Enter item code to search": "Enter item code to search", "Enter item code to search": "Enter item code to search",
"Failed to lookup source DO": "Failed to lookup source DO",
"Failed to lookup source DO": "Failed to match original DO",
"Item": "Item", "Item": "Item",
"Lookup": "Lookup",
"Lookup": "Match DO",
"No draft rows to submit": "No draft rows to submit", "No draft rows to submit": "No draft rows to submit",
"Only completed delivery orders can be used as replenishment source.": "Only completed delivery orders can be used as replenishment source.",
"Only completed delivery orders can be used as replenishment source.": "Only completed delivery orders can be used as the original DO.",
"Original Shipment Qty": "Original Shipment Qty", "Original Shipment Qty": "Original Shipment Qty",
"Please lookup source DO first": "Please lookup source DO first",
"Original Shipment Qty short": "Orig. Qty",
"Please lookup source DO first": "Please match original DO first",
"Picker Name": "Picker Name", "Picker Name": "Picker Name",
"Please select an item": "Please select an item", "Please select an item": "Please select an item",
"Records saved locally for preview. Backend integration pending.": "Records saved locally for preview. Backend integration pending.", "Records saved locally for preview. Backend integration pending.": "Records saved locally for preview. Backend integration pending.",
"Replenishment Code": "Replenishment No.", "Replenishment Code": "Replenishment No.",
"Ref Code": "Ref Code", "Ref Code": "Ref Code",
"Replenish Qty": "Replenish Qty", "Replenish Qty": "Replenish Qty",
"Replenish Qty short": "Replenish",
"Replenish qty must be greater than zero": "Replenish qty must be greater than zero", "Replenish qty must be greater than zero": "Replenish qty must be greater than zero",
"Replenishment": "Replenishment", "Replenishment": "Replenishment",
"Delivery date is required": "Delivery date is required", "Delivery date is required": "Delivery date is required",
@@ -111,11 +119,18 @@
"replenishmentDatePlaceholder": "YYYY-MM-DD", "replenishmentDatePlaceholder": "YYYY-MM-DD",
"replenishmentDoSuffixPlaceholder": "DO No. (last 4)", "replenishmentDoSuffixPlaceholder": "DO No. (last 4)",
"replenishmentShopPlaceholder": "Shop Code", "replenishmentShopPlaceholder": "Shop Code",
"Source DO": "Source DO",
"Source DO Code": "Source DO Code",
"Source DO code is required": "Source DO code is required",
"Source DO must be completed": "Source DO must be completed",
"Source DO not found": "Source DO not found",
"replenishmentRemarkPlaceholder": "Optional",
"replenishmentRemarkShort": "Optional",
"replenishmentReason": {
"quality_issue": "Quality issue",
"out_of_stock": "Out of stock",
"other": "Other"
},
"Source DO": "Original DO",
"Source DO Code": "Original DO Code",
"Source DO code is required": "Original DO code is required",
"Source DO must be completed": "Original DO must be completed",
"Source DO not found": "Original DO not found",
"Submit": "Submit", "Submit": "Submit",
"Target DO": "Target DO", "Target DO": "Target DO",
"This item is already in the draft list": "This item is already in the draft list", "This item is already in the draft list": "This item is already in the draft list",
@@ -144,6 +159,7 @@
"Supplier Name": "Supplier Name", "Supplier Name": "Supplier Name",
"Truck Availability Warning": "Truck Availability Warning", "Truck Availability Warning": "Truck Availability Warning",
"Truck Lance Code": "Truck Lance Code", "Truck Lance Code": "Truck Lance Code",
"Truck Lane": "Truck Lane",
"Truck X": "Truck X", "Truck X": "Truck X",
"Truck lane search requires date message": "Truck lane search requires date message", "Truck lane search requires date message": "Truck lane search requires date message",
"Truck lane search requires date title": "Truck lane search requires date title", "Truck lane search requires date title": "Truck lane search requires date title",


+ 28
- 12
src/i18n/zh/do.json Voir le fichier

@@ -19,26 +19,34 @@
"Enter last 4 characters of DO code": "請輸入送貨單號末四位", "Enter last 4 characters of DO code": "請輸入送貨單號末四位",
"Enter item code to search": "輸入貨品編號搜尋", "Enter item code to search": "輸入貨品編號搜尋",
"Shop code, or first characters of shop name": "店鋪代碼(部分符合),或店鋪名稱開頭字元", "Shop code, or first characters of shop name": "店鋪代碼(部分符合),或店鋪名稱開頭字元",
"Multiple source DOs matched": "找到多張符合的來源送貨單",
"Multiple source DOs matched": "找到多張符合的送貨單",
"Please verify DO code suffix, delivery date and shop.": "請核對送貨單號末四位、送貨日及店鋪資料。", "Please verify DO code suffix, delivery date and shop.": "請核對送貨單號末四位、送貨日及店鋪資料。",
"Shop code or name is required": "請輸入店鋪代碼或名稱", "Shop code or name is required": "請輸入店鋪代碼或名稱",
"Draft List": "待提交列表", "Draft List": "待提交列表",
"Replenishment preview hint": "可從不同來源送貨單加入品項,在此批次提交。",
"Replenishment preview empty": "加入的品項會顯示於此;可再查詢其他來源送貨單繼續加入。",
"Replenishment preview hint": "可從不同原送貨單加入品項,在此批次提交。",
"Replenishment preview empty": "加入的品項會顯示於此;可再對單其他原送貨單繼續加入。",
"replenishmentCurrentDoDraftHint": "已加入待提交列表(此送貨單 {{count}} 項)",
"replenishmentTargetDoEstimatedArrivalDate": "目標送貨單預計送貨日期",
"replenishmentOriginalSourceDoCode": "原送貨單編號",
"replenishmentTargetDoCode": "目標送貨單編號",
"replenishmentStatusLabel": "補貨狀態",
"replenishmentItemInfo": "貨品資訊",
"Clear": "清空", "Clear": "清空",
"Failed to lookup source DO": "查詢來源送貨單失敗",
"Failed to lookup source DO": "原送貨單對單失敗",
"Item": "物品", "Item": "物品",
"Lookup": "查詢",
"Lookup": "對單",
"No draft rows to submit": "沒有待提交的行", "No draft rows to submit": "沒有待提交的行",
"Only completed delivery orders can be used as replenishment source.": "只有已送貨(completed)的送貨單可作為補貨來源。",
"Only completed delivery orders can be used as replenishment source.": "只有已送貨(completed)的送貨單可作為原送貨單。",
"Original Shipment Qty": "原出貨數", "Original Shipment Qty": "原出貨數",
"Please lookup source DO first": "請先查詢來源送貨單",
"Original Shipment Qty short": "原出貨",
"Please lookup source DO first": "請先對單(原送貨單)",
"Picker Name": "揀貨員名稱", "Picker Name": "揀貨員名稱",
"Please select an item": "請選擇物品", "Please select an item": "請選擇物品",
"Records saved locally for preview. Backend integration pending.": "記錄已暫存於本地預覽,後端 API 尚未就緒。", "Records saved locally for preview. Backend integration pending.": "記錄已暫存於本地預覽,後端 API 尚未就緒。",
"Replenishment Code": "補貨編號", "Replenishment Code": "補貨編號",
"Ref Code": "參考編號", "Ref Code": "參考編號",
"Replenish Qty": "補貨數量", "Replenish Qty": "補貨數量",
"Replenish Qty short": "補貨",
"Replenish qty must be greater than zero": "補貨數量必須大於零", "Replenish qty must be greater than zero": "補貨數量必須大於零",
"Replenishment": "補貨", "Replenishment": "補貨",
"Delivery date is required": "請選擇送貨日期", "Delivery date is required": "請選擇送貨日期",
@@ -56,11 +64,18 @@
"replenishmentDatePlaceholder": "YYYY-MM-DD", "replenishmentDatePlaceholder": "YYYY-MM-DD",
"replenishmentDoSuffixPlaceholder": "送貨單號末四位", "replenishmentDoSuffixPlaceholder": "送貨單號末四位",
"replenishmentShopPlaceholder": "店鋪編號", "replenishmentShopPlaceholder": "店鋪編號",
"Source DO": "來源送貨單",
"Source DO Code": "來源送貨單編號",
"Source DO code is required": "請輸入來源送貨單編號",
"Source DO must be completed": "來源送貨單須為已送貨狀態",
"Source DO not found": "找不到來源送貨單",
"replenishmentRemarkPlaceholder": "請選擇(選填)",
"replenishmentRemarkShort": "選填",
"replenishmentReason": {
"quality_issue": "質素問題",
"out_of_stock": "缺貨",
"other": "其他"
},
"Source DO": "原送貨單",
"Source DO Code": "原送貨單編號",
"Source DO code is required": "請輸入原送貨單編號",
"Source DO must be completed": "原送貨單須為已送貨狀態",
"Source DO not found": "找不到原送貨單",
"Submit": "提交", "Submit": "提交",
"Target DO": "目標送貨單", "Target DO": "目標送貨單",
"This item is already in the draft list": "此物品已在待提交列表中", "This item is already in the draft list": "此物品已在待提交列表中",
@@ -83,6 +98,7 @@
"Truck lane search requires date title": "需選擇預計送貨日期", "Truck lane search requires date title": "需選擇預計送貨日期",
"Truck lane search requires date message": "已填寫車線號碼時,請一併選擇預計送貨日期後再搜索。", "Truck lane search requires date message": "已填寫車線號碼時,請一併選擇預計送貨日期後再搜索。",
"Truck Lance Code": "車線號碼", "Truck Lance Code": "車線號碼",
"Truck Lane": "車線",
"Select Remark": "選擇備註", "Select Remark": "選擇備註",
"Confirm Assignment": "確認分配", "Confirm Assignment": "確認分配",
"Submit Qty": "提交數量", "Submit Qty": "提交數量",


Chargement…
Annuler
Enregistrer