| @@ -4,7 +4,7 @@ import LaserPrintSearch from "@/components/LaserPrint/LaserPrintSearch"; | |||||
| import { Stack, Typography } from "@mui/material"; | import { Stack, Typography } from "@mui/material"; | ||||
| export const metadata: Metadata = { | export const metadata: Metadata = { | ||||
| title: "檸檬機(激光機)", | |||||
| title: "激光機", | |||||
| }; | }; | ||||
| export default async function LaserPrintPage() { | export default async function LaserPrintPage() { | ||||
| @@ -12,7 +12,7 @@ export default async function LaserPrintPage() { | |||||
| <I18nProvider namespaces={["laserPrint", "navigation", "common"]}> | <I18nProvider namespaces={["laserPrint", "navigation", "common"]}> | ||||
| <Stack direction="row" justifyContent="space-between" flexWrap="wrap" rowGap={2}> | <Stack direction="row" justifyContent="space-between" flexWrap="wrap" rowGap={2}> | ||||
| <Typography variant="h4" marginInlineEnd={2}> | <Typography variant="h4" marginInlineEnd={2}> | ||||
| 檸檬機(激光機) | |||||
| 激光機 | |||||
| </Typography> | </Typography> | ||||
| </Stack> | </Stack> | ||||
| <LaserPrintSearch /> | <LaserPrintSearch /> | ||||
| @@ -318,7 +318,7 @@ export default function M18SynPage() { | |||||
| label="PO Code" | label="PO Code" | ||||
| value={m18PoCode} | value={m18PoCode} | ||||
| onChange={(e) => setM18PoCode(e.target.value)} | onChange={(e) => setM18PoCode(e.target.value)} | ||||
| placeholder="e.g. PFP002PO26030341" | |||||
| placeholder="e.g. DEMO-PO-001" | |||||
| sx={{ minWidth: 320 }} | sx={{ minWidth: 320 }} | ||||
| /> | /> | ||||
| <Button variant="contained" color="primary" onClick={handleSyncM18PoByCode} disabled={isSyncingM18Po}> | <Button variant="contained" color="primary" onClick={handleSyncM18PoByCode} disabled={isSyncingM18Po}> | ||||
| @@ -418,7 +418,7 @@ export default function M18SynPage() { | |||||
| label="Item / product code" | label="Item / product code" | ||||
| value={m18ProductCode} | value={m18ProductCode} | ||||
| onChange={(e) => setM18ProductCode(e.target.value)} | onChange={(e) => setM18ProductCode(e.target.value)} | ||||
| placeholder="e.g. PP1175 (M18 item code)" | |||||
| placeholder="e.g. DEMO-FG-01" | |||||
| sx={{ minWidth: 320 }} | sx={{ minWidth: 320 }} | ||||
| /> | /> | ||||
| <Button | <Button | ||||
| @@ -1179,9 +1179,9 @@ export default function ProductionSchedulePage() { | |||||
| <th className="px-4 py-2 text-left font-bold text-slate-700 dark:text-slate-200">設定排期庫存</th> | <th className="px-4 py-2 text-left font-bold text-slate-700 dark:text-slate-200">設定排期庫存</th> | ||||
| <th className="px-4 py-2 text-right font-bold text-slate-700 dark:text-slate-200">過去平均出貨量</th> | <th className="px-4 py-2 text-right font-bold text-slate-700 dark:text-slate-200">過去平均出貨量</th> | ||||
| <th className="px-4 py-2 text-left font-bold text-slate-700 dark:text-slate-200">設定排期每天出貨量</th> | <th className="px-4 py-2 text-left font-bold text-slate-700 dark:text-slate-200">設定排期每天出貨量</th> | ||||
| <th className="px-4 py-2 text-center font-bold text-slate-700 dark:text-slate-200">咖啡</th> | |||||
| <th className="px-4 py-2 text-center font-bold text-slate-700 dark:text-slate-200">茶</th> | |||||
| <th className="px-4 py-2 text-center font-bold text-slate-700 dark:text-slate-200">檸檬</th> | |||||
| <th className="px-4 py-2 text-center font-bold text-slate-700 dark:text-slate-200">類別 A</th> | |||||
| <th className="px-4 py-2 text-center font-bold text-slate-700 dark:text-slate-200">類別 B</th> | |||||
| <th className="px-4 py-2 text-center font-bold text-slate-700 dark:text-slate-200">類別 C</th> | |||||
| </tr> | </tr> | ||||
| </thead> | </thead> | ||||
| <tbody> | <tbody> | ||||
| @@ -239,7 +239,7 @@ const AsyncItemCodeAutocomplete: React.FC<Props> = ({ | |||||
| {...params} | {...params} | ||||
| fullWidth | fullWidth | ||||
| label={label} | label={label} | ||||
| placeholder={hasSelection ? "" : (placeholder || "e.g. FA0591")} | |||||
| placeholder={hasSelection ? "" : (placeholder || "e.g. DEMO-FG-01")} | |||||
| helperText={t("typeToSearchItemCode", { min: minChars })} | helperText={t("typeToSearchItemCode", { min: minChars })} | ||||
| InputLabelProps={params.InputLabelProps} | InputLabelProps={params.InputLabelProps} | ||||
| InputProps={{ | InputProps={{ | ||||
| @@ -75,7 +75,7 @@ export default function ItemQcReportFilters({ | |||||
| label={field("itemCode", "貨品編號 Item Code")} | label={field("itemCode", "貨品編號 Item Code")} | ||||
| value={criteria.itemCode || ""} | value={criteria.itemCode || ""} | ||||
| onChange={(e) => onFieldChange("itemCode", e.target.value)} | onChange={(e) => onFieldChange("itemCode", e.target.value)} | ||||
| placeholder="e.g. MJ0364" | |||||
| placeholder="e.g. DEMO-FG-01" | |||||
| /> | /> | ||||
| </Grid> | </Grid> | ||||
| <Grid item {...gridSize}> | <Grid item {...gridSize}> | ||||
| @@ -159,7 +159,7 @@ export default function ReportPage() { | |||||
| if (reportId === 'rep-010') { | if (reportId === 'rep-010') { | ||||
| updateCriteria({ qcType: 'all', qcItemScope: 'all' }); | updateCriteria({ qcType: 'all', qcItemScope: 'all' }); | ||||
| } else if (reportId === 'rep-004') { | } else if (reportId === 'rep-004') { | ||||
| updateCriteria({ storeId: 'All', poPrefix: 'All' }); | |||||
| updateCriteria({ storeId: 'All' }); | |||||
| } else if (reportId === 'rep-021') { | } else if (reportId === 'rep-021') { | ||||
| updateCriteria({ storeId: 'All', stockTakeSectionDescription: 'All', lotOrigin: 'All' }); | updateCriteria({ storeId: 'All', stockTakeSectionDescription: 'All', lotOrigin: 'All' }); | ||||
| } else { | } else { | ||||
| @@ -727,7 +727,7 @@ export default function ReportPage() { | |||||
| <Grid item {...gridSize} key={fieldKey}> | <Grid item {...gridSize} key={fieldKey}> | ||||
| <AsyncItemCodeAutocomplete | <AsyncItemCodeAutocomplete | ||||
| label={translatedLabel} | label={translatedLabel} | ||||
| placeholder={field.placeholder || "e.g. FA0591"} | |||||
| placeholder={field.placeholder || "e.g. DEMO-FG-01"} | |||||
| value={selectedCodes} | value={selectedCodes} | ||||
| onInputValueChange={(text) => { | onInputValueChange={(text) => { | ||||
| pendingItemCodeInputRef.current[field.name] = text; | pendingItemCodeInputRef.current[field.name] = text; | ||||
| @@ -33,6 +33,6 @@ export const REPORT_CATEGORIES: ReportCategoryConfig[] = [ | |||||
| headerBg: "#f5d4a8", | headerBg: "#f5d4a8", | ||||
| bodyBg: "#fdf6ec", | bodyBg: "#fdf6ec", | ||||
| accent: "#e65100", | accent: "#e65100", | ||||
| reportIds: ["rep-006", "rep-005", "rep-015"], | |||||
| reportIds: ["rep-006", "rep-005"], | |||||
| }, | }, | ||||
| ]; | ]; | ||||
| @@ -560,7 +560,7 @@ export default function TestingPage() { | |||||
| <Alert severity="info" sx={{ mb: 2 }}> | <Alert severity="info" sx={{ mb: 2 }}> | ||||
| Uses <strong>GET /py/job-orders?planStart=</strong> for the day, | Uses <strong>GET /py/job-orders?planStart=</strong> for the day, | ||||
| then the same <code>jobOrders</code> payload as{" "} | then the same <code>jobOrders</code> payload as{" "} | ||||
| <strong>Bag Print → 下載 OnPack2023檸檬機</strong>. The ZIP contains | |||||
| <strong>Bag Print → 下載 OnPack 文字</strong>. The ZIP contains | |||||
| loose <code>.job</code> / <code>.image</code> / BMPs — extract | loose <code>.job</code> / <code>.image</code> / BMPs — extract | ||||
| before sending to NGE; the ZIP itself is only a transport bundle. | before sending to NGE; the ZIP itself is only a transport bundle. | ||||
| </Alert> | </Alert> | ||||
| @@ -973,12 +973,9 @@ export default function TestingPage() { | |||||
| <TabPanel value={tabValue} index={5}> | <TabPanel value={tabValue} index={5}> | ||||
| <Section title="6. M18 sync alert email"> | <Section title="6. M18 sync alert email"> | ||||
| <Alert severity="info" sx={{ mb: 2 }}> | <Alert severity="info" sx={{ mb: 2 }}> | ||||
| Production sync errors email{" "} | |||||
| <strong>[email protected]</strong> and{" "} | |||||
| <strong>[email protected]</strong> (see{" "} | |||||
| <code>scheduler.sync-alert.email.to-addresses</code>). WhatsApp/Twilio | |||||
| is disabled. SMTP from DB <code>MAIL.smtp.*</code> (e.g. Gmail{" "} | |||||
| <code>[email protected]</code> + app password). | |||||
| Production sync errors email the addresses in{" "} | |||||
| <code>scheduler.sync-alert.email.to-addresses</code>. WhatsApp/Twilio | |||||
| is disabled. SMTP comes from DB <code>MAIL.smtp.*</code>. | |||||
| </Alert> | </Alert> | ||||
| <Stack spacing={2} sx={{ mb: 2, maxWidth: 720 }}> | <Stack spacing={2} sx={{ mb: 2, maxWidth: 720 }}> | ||||
| <TextField | <TextField | ||||
| @@ -180,7 +180,7 @@ function skippedWithoutExpiryFromResponse(res: Response): string[] { | |||||
| .filter(Boolean); | .filter(Boolean); | ||||
| } | } | ||||
| /** 汁水機 OnPack — same as QR ZIP, plus LOGO_EXP BMP from item_default_shelf_life. */ | |||||
| /** OnPack QR — same as QR ZIP, plus LOGO_EXP BMP from item_default_shelf_life. */ | |||||
| export async function downloadOnPackQrZipWithExpiry( | export async function downloadOnPackQrZipWithExpiry( | ||||
| request: OnPackQrDownloadRequest, | request: OnPackQrDownloadRequest, | ||||
| ): Promise<OnPackZipDownload> { | ): Promise<OnPackZipDownload> { | ||||
| @@ -201,7 +201,7 @@ export async function downloadOnPackQrZipWithExpiry( | |||||
| }; | }; | ||||
| } | } | ||||
| /** OnPack2023 檸檬機 — text QR template (`onpack2030_2`), no separate .bmp */ | |||||
| /** OnPack text QR template (`onpack2030_2`), no separate .bmp */ | |||||
| export async function downloadOnPackTextQrZip( | export async function downloadOnPackTextQrZip( | ||||
| request: OnPackQrDownloadRequest, | request: OnPackQrDownloadRequest, | ||||
| ): Promise<Blob> { | ): Promise<Blob> { | ||||
| @@ -219,7 +219,7 @@ export async function downloadOnPackTextQrZip( | |||||
| return res.blob(); | return res.blob(); | ||||
| } | } | ||||
| /** OnPack2023 檸檬機 — same as text ZIP, plus TEXT_EXP from item_default_shelf_life. */ | |||||
| /** OnPack text QR — same as text ZIP, plus TEXT_EXP from item_default_shelf_life. */ | |||||
| export async function downloadOnPackTextQrZipWithExpiry( | export async function downloadOnPackTextQrZipWithExpiry( | ||||
| request: OnPackQrDownloadRequest, | request: OnPackQrDownloadRequest, | ||||
| ): Promise<OnPackZipDownload> { | ): Promise<OnPackZipDownload> { | ||||
| @@ -82,15 +82,23 @@ const SETTINGS_KEY = "bagPrint_settings"; | |||||
| const DEFAULT_SETTINGS = { | const DEFAULT_SETTINGS = { | ||||
| dabag_ip: "", | dabag_ip: "", | ||||
| dabag_port: "3008", | dabag_port: "3008", | ||||
| laser_ip: "192.168.17.10", | |||||
| laser_ip: "", | |||||
| laser_port: "45678", | laser_port: "45678", | ||||
| }; | }; | ||||
| const LIVE_PRINTER_HOSTS = new Set(["192.168.17.10", "192.168.18.77"]); | |||||
| function loadSettings(): typeof DEFAULT_SETTINGS { | function loadSettings(): typeof DEFAULT_SETTINGS { | ||||
| if (typeof window === "undefined") return DEFAULT_SETTINGS; | if (typeof window === "undefined") return DEFAULT_SETTINGS; | ||||
| try { | try { | ||||
| const s = localStorage.getItem(SETTINGS_KEY); | const s = localStorage.getItem(SETTINGS_KEY); | ||||
| if (s) return { ...DEFAULT_SETTINGS, ...JSON.parse(s) }; | |||||
| if (s) { | |||||
| const merged = { ...DEFAULT_SETTINGS, ...JSON.parse(s) }; | |||||
| if (LIVE_PRINTER_HOSTS.has(String(merged.laser_ip ?? "").trim())) { | |||||
| merged.laser_ip = ""; | |||||
| } | |||||
| return merged; | |||||
| } | |||||
| } catch {} | } catch {} | ||||
| return DEFAULT_SETTINGS; | return DEFAULT_SETTINGS; | ||||
| } | } | ||||
| @@ -467,11 +475,11 @@ const BagPrintSearch: React.FC = () => { | |||||
| link.remove(); | link.remove(); | ||||
| window.URL.revokeObjectURL(url); | window.URL.revokeObjectURL(url); | ||||
| setSnackbar(skippedExpirySnackbar("OnPack 汁水機(含到期日)ZIP 已下載", skippedWithoutExpiry)); | |||||
| setSnackbar(skippedExpirySnackbar("OnPack QR(含到期日)ZIP 已下載", skippedWithoutExpiry)); | |||||
| } catch (e) { | } catch (e) { | ||||
| setSnackbar({ | setSnackbar({ | ||||
| open: true, | open: true, | ||||
| message: e instanceof Error ? e.message : "下載 OnPack 汁水機(含到期日)失敗", | |||||
| message: e instanceof Error ? e.message : "下載 OnPack QR(含到期日)失敗", | |||||
| severity: "error", | severity: "error", | ||||
| }); | }); | ||||
| } finally { | } finally { | ||||
| @@ -505,11 +513,11 @@ const BagPrintSearch: React.FC = () => { | |||||
| link.remove(); | link.remove(); | ||||
| window.URL.revokeObjectURL(url); | window.URL.revokeObjectURL(url); | ||||
| setSnackbar({ open: true, message: "OnPack2023檸檬機 ZIP 已下載", severity: "success" }); | |||||
| setSnackbar({ open: true, message: "OnPack 文字 ZIP 已下載", severity: "success" }); | |||||
| } catch (e) { | } catch (e) { | ||||
| setSnackbar({ | setSnackbar({ | ||||
| open: true, | open: true, | ||||
| message: e instanceof Error ? e.message : "下載 OnPack2023檸檬機 失敗", | |||||
| message: e instanceof Error ? e.message : "下載 OnPack 文字 失敗", | |||||
| severity: "error", | severity: "error", | ||||
| }); | }); | ||||
| } finally { | } finally { | ||||
| @@ -544,11 +552,11 @@ const BagPrintSearch: React.FC = () => { | |||||
| link.remove(); | link.remove(); | ||||
| window.URL.revokeObjectURL(url); | window.URL.revokeObjectURL(url); | ||||
| setSnackbar(skippedExpirySnackbar("OnPack2023檸檬機(含到期日)ZIP 已下載", skippedWithoutExpiry)); | |||||
| setSnackbar(skippedExpirySnackbar("OnPack 文字(含到期日)ZIP 已下載", skippedWithoutExpiry)); | |||||
| } catch (e) { | } catch (e) { | ||||
| setSnackbar({ | setSnackbar({ | ||||
| open: true, | open: true, | ||||
| message: e instanceof Error ? e.message : "下載 OnPack2023檸檬機(含到期日)失敗", | |||||
| message: e instanceof Error ? e.message : "下載 OnPack 文字(含到期日)失敗", | |||||
| severity: "error", | severity: "error", | ||||
| }); | }); | ||||
| } finally { | } finally { | ||||
| @@ -597,7 +605,7 @@ const BagPrintSearch: React.FC = () => { | |||||
| ), | ), | ||||
| ); | ); | ||||
| } catch { | } catch { | ||||
| /* 檸檬機標籤可沒有;不擋畫面 */ | |||||
| /* 激光機標籤可沒有;不擋畫面 */ | |||||
| } | } | ||||
| })(); | })(); | ||||
| }, []); | }, []); | ||||
| @@ -793,7 +801,7 @@ const BagPrintSearch: React.FC = () => { | |||||
| jobOrders.length === 0 | jobOrders.length === 0 | ||||
| } | } | ||||
| > | > | ||||
| {downloadingOnPack ? "下載中..." : "下載 OnPack 汁水機 QR code"} | |||||
| {downloadingOnPack ? "下載中..." : "下載 OnPack QR code"} | |||||
| </Button> | </Button> | ||||
| <Button | <Button | ||||
| variant="contained" | variant="contained" | ||||
| @@ -808,7 +816,7 @@ const BagPrintSearch: React.FC = () => { | |||||
| jobOrders.length === 0 | jobOrders.length === 0 | ||||
| } | } | ||||
| > | > | ||||
| {downloadingOnPackExp ? "下載中..." : "下載 OnPack 汁水機(含到期日)"} | |||||
| {downloadingOnPackExp ? "下載中..." : "下載 OnPack QR(含到期日)"} | |||||
| </Button> | </Button> | ||||
| <Button | <Button | ||||
| variant="contained" | variant="contained" | ||||
| @@ -824,7 +832,7 @@ const BagPrintSearch: React.FC = () => { | |||||
| jobOrders.length === 0 | jobOrders.length === 0 | ||||
| } | } | ||||
| > | > | ||||
| {downloadingOnPackText ? "下載中..." : "下載 OnPack2023檸檬機"} | |||||
| {downloadingOnPackText ? "下載中..." : "下載 OnPack 文字"} | |||||
| </Button> | </Button> | ||||
| {canSeeOnPackAdmin && ( | {canSeeOnPackAdmin && ( | ||||
| <Button | <Button | ||||
| @@ -841,7 +849,7 @@ const BagPrintSearch: React.FC = () => { | |||||
| jobOrders.length === 0 | jobOrders.length === 0 | ||||
| } | } | ||||
| > | > | ||||
| {downloadingOnPackTextExp ? "下載中..." : "下載 OnPack2023檸檬機(含到期日)"} | |||||
| {downloadingOnPackTextExp ? "下載中..." : "下載 OnPack 文字(含到期日)"} | |||||
| </Button> | </Button> | ||||
| )} | )} | ||||
| </Stack> | </Stack> | ||||
| @@ -903,8 +911,8 @@ const BagPrintSearch: React.FC = () => { | |||||
| {jo.itemCode || "—"} | {jo.itemCode || "—"} | ||||
| </Typography> | </Typography> | ||||
| <Stack direction="row" spacing={0.5} flexWrap="wrap" useFlexGap sx={{ mt: 0.5 }}> | <Stack direction="row" spacing={0.5} flexWrap="wrap" useFlexGap sx={{ mt: 0.5 }}> | ||||
| {juiceExpiryOk ? <Chip size="small" label="汁水機" color="primary" /> : null} | |||||
| {lemonOk ? <Chip size="small" label="檸檬機" color="secondary" /> : null} | |||||
| {juiceExpiryOk ? <Chip size="small" label="OnPack QR" color="primary" /> : null} | |||||
| {lemonOk ? <Chip size="small" label="激光機" color="secondary" /> : null} | |||||
| </Stack> | </Stack> | ||||
| </Box> | </Box> | ||||
| <Box sx={{ flex: 1, minWidth: 0 }}> | <Box sx={{ flex: 1, minWidth: 0 }}> | ||||
| @@ -1086,17 +1094,17 @@ const BagPrintSearch: React.FC = () => { | |||||
| > | > | ||||
| <Stack spacing={1.5} sx={{ flexShrink: 0, mb: 1 }}> | <Stack spacing={1.5} sx={{ flexShrink: 0, mb: 1 }}> | ||||
| <Typography variant="subtitle2" color="primary"> | <Typography variant="subtitle2" color="primary"> | ||||
| 汁水機({expiryCodes.length}) | |||||
| OnPack QR({expiryCodes.length}) | |||||
| </Typography> | </Typography> | ||||
| <Typography variant="body2" color="text.secondary"> | <Typography variant="body2" color="text.secondary"> | ||||
| 「下載 OnPack 汁水機(含到期日)」只用此清單。名稱印在 Product 行,過長可改短後按儲存。 | |||||
| 「下載 OnPack QR(含到期日)」只用此清單。名稱印在 Product 行,過長可改短後按儲存。 | |||||
| 點欄位標題可排序;冷藏/-18/列印第一次點擊會把「未設」排到最前。 | 點欄位標題可排序;冷藏/-18/列印第一次點擊會把「未設」排到最前。 | ||||
| </Typography> | </Typography> | ||||
| <Stack direction="row" spacing={1} alignItems="center"> | <Stack direction="row" spacing={1} alignItems="center"> | ||||
| <TextField | <TextField | ||||
| label="新增品號" | label="新增品號" | ||||
| size="small" | size="small" | ||||
| placeholder="例如 PP2211" | |||||
| placeholder="例如 DEMO-FG-01" | |||||
| value={expiryCodeInput} | value={expiryCodeInput} | ||||
| onChange={(e) => setExpiryCodeInput(e.target.value)} | onChange={(e) => setExpiryCodeInput(e.target.value)} | ||||
| onKeyDown={(e) => { | onKeyDown={(e) => { | ||||
| @@ -1216,7 +1224,7 @@ const BagPrintSearch: React.FC = () => { | |||||
| onChange={(e) => | onChange={(e) => | ||||
| setNameDrafts((prev) => ({ ...prev, [row.itemCode]: e.target.value })) | setNameDrafts((prev) => ({ ...prev, [row.itemCode]: e.target.value })) | ||||
| } | } | ||||
| placeholder={row.defaultPrintName || "例如 豬肉水(1包X2千克)"} | |||||
| placeholder={row.defaultPrintName || "例如 鄉村麵包"} | |||||
| inputProps={{ maxLength: 255 }} | inputProps={{ maxLength: 255 }} | ||||
| fullWidth | fullWidth | ||||
| /> | /> | ||||
| @@ -1292,7 +1300,7 @@ const BagPrintSearch: React.FC = () => { | |||||
| </TableContainer> | </TableContainer> | ||||
| )} | )} | ||||
| <Typography variant="body2" color="text.secondary" sx={{ flexShrink: 0, mt: 1 }}> | <Typography variant="body2" color="text.secondary" sx={{ flexShrink: 0, mt: 1 }}> | ||||
| 檸檬機到期日 ZIP 品號稍後加入。 | |||||
| 激光機到期日 ZIP 品號稍後加入。 | |||||
| </Typography> | </Typography> | ||||
| </DialogContent> | </DialogContent> | ||||
| <DialogActions> | <DialogActions> | ||||
| @@ -358,8 +358,8 @@ const DeliveryOrderFloorSettings: React.FC = () => { | |||||
| onChange={onFloorSelectChange} | onChange={onFloorSelectChange} | ||||
| disabled={dialogSaving || comboLoading} | disabled={dialogSaving || comboLoading} | ||||
| > | > | ||||
| <MenuItem value="2F">2F</MenuItem> | |||||
| <MenuItem value="4F">4F</MenuItem> | |||||
| <MenuItem value="2F">2/F</MenuItem> | |||||
| <MenuItem value="4F">4/F</MenuItem> | |||||
| </Select> | </Select> | ||||
| </FormControl> | </FormControl> | ||||
| <Button | <Button | ||||
| @@ -83,21 +83,27 @@ const LaserPrintSearch: React.FC = () => { | |||||
| message: "", | message: "", | ||||
| }); | }); | ||||
| const [successSignal, setSuccessSignal] = useState<string | null>(null); | const [successSignal, setSuccessSignal] = useState<string | null>(null); | ||||
| const [laserHost, setLaserHost] = useState("192.168.18.77"); | |||||
| const [laserHost, setLaserHost] = useState(""); | |||||
| const [laserPort, setLaserPort] = useState("45678"); | const [laserPort, setLaserPort] = useState("45678"); | ||||
| const [laserItemCodes, setLaserItemCodes] = useState("PP1175"); | |||||
| const [laserItemCodes, setLaserItemCodes] = useState(""); | |||||
| const [lastLaserReceive, setLastLaserReceive] = useState<LaserLastReceiveSuccess | null>(null); | const [lastLaserReceive, setLastLaserReceive] = useState<LaserLastReceiveSuccess | null>(null); | ||||
| const [settingsLoaded, setSettingsLoaded] = useState(false); | const [settingsLoaded, setSettingsLoaded] = useState(false); | ||||
| const [printerConnected, setPrinterConnected] = useState(false); | const [printerConnected, setPrinterConnected] = useState(false); | ||||
| const [printerMessage, setPrinterMessage] = useState("檸檬機(激光機)未連接"); | |||||
| const [printerMessage, setPrinterMessage] = useState("激光機未連接"); | |||||
| const sendInFlightRef = useRef(false); | const sendInFlightRef = useRef(false); | ||||
| const loadSystemSettings = useCallback(async () => { | const loadSystemSettings = useCallback(async () => { | ||||
| try { | try { | ||||
| const s = await fetchLaserBag2Settings(); | const s = await fetchLaserBag2Settings(); | ||||
| setLaserHost(s.host); | |||||
| const host = (s.host ?? "").trim(); | |||||
| setLaserHost(host === "192.168.18.77" || host === "192.168.17.10" ? "" : host); | |||||
| setLaserPort(String(s.port)); | setLaserPort(String(s.port)); | ||||
| setLaserItemCodes(s.itemCodes ?? "PP1175"); | |||||
| const itemCodes = (s.itemCodes ?? "") | |||||
| .split(",") | |||||
| .map((code) => code.trim()) | |||||
| .filter((code) => code && code !== "PP1175" && code !== "PP2211") | |||||
| .join(","); | |||||
| setLaserItemCodes(itemCodes); | |||||
| setLastLaserReceive(s.lastReceiveSuccess ?? null); | setLastLaserReceive(s.lastReceiveSuccess ?? null); | ||||
| setSettingsLoaded(true); | setSettingsLoaded(true); | ||||
| } catch (e) { | } catch (e) { | ||||
| @@ -158,7 +164,7 @@ const LaserPrintSearch: React.FC = () => { | |||||
| setPrinterMessage(result.message); | setPrinterMessage(result.message); | ||||
| } catch (e) { | } catch (e) { | ||||
| setPrinterConnected(false); | setPrinterConnected(false); | ||||
| setPrinterMessage(e instanceof Error ? e.message : "檸檬機(激光機)狀態檢查失敗"); | |||||
| setPrinterMessage(e instanceof Error ? e.message : "激光機狀態檢查失敗"); | |||||
| } | } | ||||
| }, []); | }, []); | ||||
| @@ -201,7 +207,7 @@ const LaserPrintSearch: React.FC = () => { | |||||
| if (sendInFlightRef.current || sendingJobId !== null) return; | if (sendInFlightRef.current || sendingJobId !== null) return; | ||||
| if (!laserHost.trim()) { | if (!laserHost.trim()) { | ||||
| setErrorSnackbar({ open: true, message: "請在系統設定中填寫檸檬機(激光機) IP。" }); | |||||
| setErrorSnackbar({ open: true, message: "請在系統設定中填寫激光機 IP。" }); | |||||
| return; | return; | ||||
| } | } | ||||
| @@ -239,8 +245,8 @@ const LaserPrintSearch: React.FC = () => { | |||||
| : ""; | : ""; | ||||
| setSuccessSignal( | setSuccessSignal( | ||||
| laterFail | laterFail | ||||
| ? `已送出 ${sentOk} 次至檸檬機(激光機)${ackHint}(後續重送失敗:${laterFail})` | |||||
| : `已送出 ${sentOk} 次至檸檬機(激光機)${ackHint}`, | |||||
| ? `已送出 ${sentOk} 次至激光機${ackHint}(後續重送失敗:${laterFail})` | |||||
| : `已送出 ${sentOk} 次至激光機${ackHint}`, | |||||
| ); | ); | ||||
| await loadSystemSettings(); | await loadSystemSettings(); | ||||
| } catch (e) { | } catch (e) { | ||||
| @@ -287,7 +293,7 @@ const LaserPrintSearch: React.FC = () => { | |||||
| {lastLaserReceive.source ? ` (${lastLaserReceive.source === "AUTO" ? "自動送出" : "手動點選"})` : ""} | {lastLaserReceive.source ? ` (${lastLaserReceive.source === "AUTO" ? "自動送出" : "手動點選"})` : ""} | ||||
| </Typography> | </Typography> | ||||
| <Typography variant="body2" sx={{ mt: 0.5 }}> | <Typography variant="body2" sx={{ mt: 0.5 }}> | ||||
| 此時間只會在檸檬機回覆 receive 時更新。之後送出失敗不會改這裡。 | |||||
| 此時間只會在激光機回覆 receive 時更新。之後送出失敗不會改這裡。 | |||||
| </Typography> | </Typography> | ||||
| </Alert> | </Alert> | ||||
| )} | )} | ||||
| @@ -332,7 +338,7 @@ const LaserPrintSearch: React.FC = () => { | |||||
| }} | }} | ||||
| title={printerMessage} | title={printerMessage} | ||||
| > | > | ||||
| 檸檬機(激光機): | |||||
| 激光機: | |||||
| </Box> | </Box> | ||||
| </Stack> | </Stack> | ||||
| </Stack> | </Stack> | ||||
| @@ -420,11 +426,11 @@ const LaserPrintSearch: React.FC = () => { | |||||
| </Paper> | </Paper> | ||||
| <Dialog open={settingsOpen} onClose={() => setSettingsOpen(false)} maxWidth="sm" fullWidth> | <Dialog open={settingsOpen} onClose={() => setSettingsOpen(false)} maxWidth="sm" fullWidth> | ||||
| <DialogTitle>檸檬機(激光機)(系統設定)</DialogTitle> | |||||
| <DialogTitle>激光機(系統設定)</DialogTitle> | |||||
| <DialogContent> | <DialogContent> | ||||
| <Stack spacing={2} sx={{ mt: 1 }}> | <Stack spacing={2} sx={{ mt: 1 }}> | ||||
| <Typography variant="body2" color="text.secondary"> | <Typography variant="body2" color="text.secondary"> | ||||
| 儲存後寫入資料庫,後端送出走此 IP/埠(預設 192.168.18.77:45678)。 | |||||
| 儲存後寫入資料庫,後端送出走此 IP/埠。未填 IP 則不會送出。 | |||||
| </Typography> | </Typography> | ||||
| <TextField | <TextField | ||||
| label="IP" | label="IP" | ||||
| @@ -446,8 +452,8 @@ const LaserPrintSearch: React.FC = () => { | |||||
| value={laserItemCodes} | value={laserItemCodes} | ||||
| onChange={(e) => setLaserItemCodes(e.target.value)} | onChange={(e) => setLaserItemCodes(e.target.value)} | ||||
| fullWidth | fullWidth | ||||
| placeholder="PP1175" | |||||
| helperText="預設 PP1175;可輸入多個品號,例如 PP1175,AB999。留空則列表顯示當日全部包裝工單。" | |||||
| placeholder="例如 DEMO-FG-01" | |||||
| helperText="可輸入多個品號,例如 DEMO-FG-01,DEMO-FG-02。留空則列表顯示當日全部包裝工單。" | |||||
| /> | /> | ||||
| </Stack> | </Stack> | ||||
| </DialogContent> | </DialogContent> | ||||
| @@ -116,7 +116,7 @@ const PutAwayForm: React.FC<Props> = ({ itemDetail, warehouse=[], disabled, sugg | |||||
| group: "default", | group: "default", | ||||
| }; | }; | ||||
| const options = useMemo(() => { | const options = useMemo(() => { | ||||
| const defaultLabel = suggestedLocationCode || t("W201 - 2F-A,B室"); | |||||
| const defaultLabel = suggestedLocationCode || t("Select warehouse"); | |||||
| return [ | return [ | ||||
| { value: 1, label: defaultLabel, group: "default" }, | { value: 1, label: defaultLabel, group: "default" }, | ||||
| ...filteredWarehouse.map((w) => ({ | ...filteredWarehouse.map((w) => ({ | ||||
| @@ -260,7 +260,7 @@ const PutAwayForm: React.FC<Props> = ({ itemDetail, warehouse=[], disabled, sugg | |||||
| editable: false, | editable: false, | ||||
| renderCell(params) { | renderCell(params) { | ||||
| const value = (params.value as string) ?? ""; | const value = (params.value as string) ?? ""; | ||||
| // 目前格式像 "2F-W201-#L-08 - 2F-W201",只要左邊 LocationCode | |||||
| // Location label is "code - description"; keep the code on the left. | |||||
| const locationCode = value.split(" - ")[0] || value; | const locationCode = value.split(" - ")[0] || value; | ||||
| return ( | return ( | ||||
| @@ -5,72 +5,67 @@ export const PO_WORKBENCH_DETAILS_GRID_MOCK_ROWS: readonly PoWorkbenchDetailsGri | |||||
| [ | [ | ||||
| { | { | ||||
| id: "mock-line-1", | id: "mock-line-1", | ||||
| itemName: | |||||
| "澳洲和牛腱(冷凍)· A5級去筋切片裝 2.5kg×4包/箱(供應商批次:AU-WG-2026-Q1)", | |||||
| itemCode: "AB1234", | |||||
| itemName: "普通麵粉", | |||||
| itemCode: "DEMO-RM-01", | |||||
| category: "material", | category: "material", | ||||
| storageLocation: "YF-W201-#A-12", | |||||
| storageLocation: "DEMO-A-#A-01", | |||||
| receiveStatus: "pending", | receiveStatus: "pending", | ||||
| expectedQty: 100, | expectedQty: 100, | ||||
| purchaseUnitLabel: "1箱X100包X150克", | |||||
| purchaseUnitLabel: "1袋X25公斤", | |||||
| receivedQtyPurchase: 0, | receivedQtyPurchase: 0, | ||||
| receivedQtyInventory: 0, | receivedQtyInventory: 0, | ||||
| inventoryUnitLabel: "克", | |||||
| inventoryUnitLabel: "公斤", | |||||
| }, | }, | ||||
| { | { | ||||
| id: "mock-line-2", | id: "mock-line-2", | ||||
| itemName: | |||||
| "耐高温真空膠袋(食品級)· 300×450mm 厚度80μm 每卷500個(雙層密封條)", | |||||
| itemCode: "BC5678", | |||||
| itemName: "鮮奶", | |||||
| itemCode: "DEMO-RM-02", | |||||
| category: "consumable", | category: "consumable", | ||||
| storageLocation: "YF-C305-#B-04", | |||||
| storageLocation: "DEMO-B-#B-02", | |||||
| receiveStatus: "receiving", | receiveStatus: "receiving", | ||||
| expectedQty: 24, | expectedQty: 24, | ||||
| purchaseUnitLabel: "1箱X24卷X500個", | |||||
| receivedQtyPurchase: 26, | |||||
| receivedQtyInventory: 312000, | |||||
| inventoryUnitLabel: "個", | |||||
| purchaseUnitLabel: "1箱X12瓶X1升", | |||||
| receivedQtyPurchase: 12, | |||||
| receivedQtyInventory: 12, | |||||
| inventoryUnitLabel: "升", | |||||
| }, | }, | ||||
| { | { | ||||
| id: "mock-line-3", | id: "mock-line-3", | ||||
| itemName: | |||||
| "照燒汁(商用濃縮裝)· 日式醬油基底 5L/桶(開封後需冷藏,效期見標籤)", | |||||
| itemCode: "FG9012", | |||||
| itemName: "鄉村麵包", | |||||
| itemCode: "DEMO-FG-01", | |||||
| category: "finishedGoods", | category: "finishedGoods", | ||||
| storageLocation: "YF-D102-#C-08", | |||||
| storageLocation: "DEMO-A-#C-03", | |||||
| receiveStatus: "completed", | receiveStatus: "completed", | ||||
| expectedQty: 48, | expectedQty: 48, | ||||
| purchaseUnitLabel: "1板X4桶X5升", | |||||
| purchaseUnitLabel: "1箱X8個", | |||||
| receivedQtyPurchase: 48, | receivedQtyPurchase: 48, | ||||
| receivedQtyInventory: 960, | |||||
| inventoryUnitLabel: "升", | |||||
| receivedQtyInventory: 48, | |||||
| inventoryUnitLabel: "個", | |||||
| }, | }, | ||||
| { | { | ||||
| id: "mock-line-4", | id: "mock-line-4", | ||||
| itemName: | |||||
| "高筋麵粉預拌漿(半成品)· 中央廚房標準配方 20kg/袋(含酵母改良劑)", | |||||
| itemCode: "DE3456", | |||||
| itemName: "麵團", | |||||
| itemCode: "DEMO-SF-01", | |||||
| category: "semiFinished", | category: "semiFinished", | ||||
| storageLocation: "YF-W118-#D-22", | |||||
| storageLocation: "DEMO-A-#D-04", | |||||
| receiveStatus: "receiving", | receiveStatus: "receiving", | ||||
| expectedQty: 60, | expectedQty: 60, | ||||
| purchaseUnitLabel: "1板X3袋X20公斤", | |||||
| receivedQtyPurchase: 55, | |||||
| receivedQtyInventory: 3300, | |||||
| purchaseUnitLabel: "1袋X10公斤", | |||||
| receivedQtyPurchase: 40, | |||||
| receivedQtyInventory: 40, | |||||
| inventoryUnitLabel: "公斤", | inventoryUnitLabel: "公斤", | ||||
| }, | }, | ||||
| { | { | ||||
| id: "mock-line-5", | id: "mock-line-5", | ||||
| itemName: | |||||
| "廚房多功能清潔劑(濃縮原液)· 無磷配方 1L×12瓶/箱(勿與漂白水混用)", | |||||
| itemCode: "EF7890", | |||||
| itemName: "奶包", | |||||
| itemCode: "DEMO-FG-02", | |||||
| category: "miscNonConsumable", | category: "miscNonConsumable", | ||||
| storageLocation: "YF-M402-#E-01", | |||||
| storageLocation: "DEMO-B-#E-05", | |||||
| receiveStatus: "pending", | receiveStatus: "pending", | ||||
| expectedQty: 120, | expectedQty: 120, | ||||
| purchaseUnitLabel: "1扎X3箱X12包X350克", | |||||
| purchaseUnitLabel: "1箱X20個", | |||||
| receivedQtyPurchase: 0, | receivedQtyPurchase: 0, | ||||
| receivedQtyInventory: 0, | receivedQtyInventory: 0, | ||||
| inventoryUnitLabel: "瓶", | |||||
| inventoryUnitLabel: "個", | |||||
| }, | }, | ||||
| ] as const; | ] as const; | ||||
| @@ -315,7 +315,7 @@ const fetchProcessDetailRef = useRef<() => Promise<void>>(); | |||||
| const processQrCode = useCallback((qrValue: string, lineId: number) => { | const processQrCode = useCallback((qrValue: string, lineId: number) => { | ||||
| // 设备快捷格式:{2fitesteXXX} - XXX 直接作为设备代码 | // 设备快捷格式:{2fitesteXXX} - XXX 直接作为设备代码 | ||||
| // 格式:{2fitesteXXX} = equipmentCode: "XXX" | // 格式:{2fitesteXXX} = equipmentCode: "XXX" | ||||
| // 例如:{2fiteste包裝機類-真空八爪魚機-1號} = equipmentCode: "包裝機類-真空八爪魚機-1號" | |||||
| // 例如:{2fitesteDEMO-EQ-01} = equipmentCode: "DEMO-EQ-01" | |||||
| if (qrValue.match(/\{2fiteste(.+)\}/)) { | if (qrValue.match(/\{2fiteste(.+)\}/)) { | ||||
| const match = qrValue.match(/\{2fiteste(.+)\}/); | const match = qrValue.match(/\{2fiteste(.+)\}/); | ||||
| const equipmentCode = match![1]; | const equipmentCode = match![1]; | ||||
| @@ -371,7 +371,7 @@ const processQrCode = useCallback((qrValue: string, lineId: number) => { | |||||
| // 可能是员工编号 | // 可能是员工编号 | ||||
| setScannedStaffNo(trimmedValue); | setScannedStaffNo(trimmedValue); | ||||
| } else if (trimmedValue.includes("-")) { | } else if (trimmedValue.includes("-")) { | ||||
| // 可能包含 "-" 的是设备代码(如 "包裝機類-真空八爪魚機-1號") | |||||
| // 可能包含 "-" 的是设备代码(如 "DEMO-EQ-01") | |||||
| setScannedEquipmentCode(trimmedValue); | setScannedEquipmentCode(trimmedValue); | ||||
| } | } | ||||
| } | } | ||||
| @@ -54,9 +54,20 @@ const asyncItemCodeField = ( | |||||
| multiple: true, | multiple: true, | ||||
| allowInput: true, | allowInput: true, | ||||
| asyncSearch: true, | asyncSearch: true, | ||||
| placeholder: "e.g. FA0591", | |||||
| placeholder: "e.g. DEMO-FG-01", | |||||
| }); | }); | ||||
| const floorOptionsAll: ReportField["options"] = [ | |||||
| { label: "全部", value: "All" }, | |||||
| { label: "2/F", value: "2/F" }, | |||||
| { label: "4/F", value: "4/F" }, | |||||
| ]; | |||||
| const floorOptions: ReportField["options"] = [ | |||||
| { label: "2/F", value: "2/F" }, | |||||
| { label: "4/F", value: "4/F" }, | |||||
| ]; | |||||
| const pasteItemCodeField = (): ReportField => ({ | const pasteItemCodeField = (): ReportField => ({ | ||||
| label: "貼上貨品編號 Paste Item Codes", | label: "貼上貨品編號 Paste Item Codes", | ||||
| name: "itemCodePaste", | name: "itemCodePaste", | ||||
| @@ -124,29 +135,12 @@ export const REPORTS: ReportDefinition[] = [ | |||||
| name: "storeId", | name: "storeId", | ||||
| type: "select", | type: "select", | ||||
| required: false, | required: false, | ||||
| options: [ | |||||
| { label: "全部", value: "All" }, | |||||
| { label: "1F", value: "1F" }, | |||||
| { label: "2F", value: "2F" }, | |||||
| { label: "3F", value: "3F" }, | |||||
| { label: "4F", value: "4F" }, | |||||
| ], | |||||
| options: floorOptionsAll, | |||||
| }, | }, | ||||
| { label: "倉庫 Warehouse", name: "warehouse", type: "text", required: false, placeholder: "e.g. W201" }, | |||||
| { label: "倉庫 Warehouse", name: "warehouse", type: "text", required: false, placeholder: "e.g. DEMO-A" }, | |||||
| { label: "區域 Area", name: "area", type: "text", required: false, placeholder: "e.g. #A" }, | { label: "區域 Area", name: "area", type: "text", required: false, placeholder: "e.g. #A" }, | ||||
| { label: "儲位 Slot", name: "slot", type: "text", required: false, placeholder: "e.g. 01" }, | { label: "儲位 Slot", name: "slot", type: "text", required: false, placeholder: "e.g. 01" }, | ||||
| { label: "批號 Lot No", name: "lotNo", type: "text", required: false, placeholder: "e.g. LT-202608" }, | { label: "批號 Lot No", name: "lotNo", type: "text", required: false, placeholder: "e.g. LT-202608" }, | ||||
| { | |||||
| label: "PP/PF 分類", | |||||
| name: "poPrefix", | |||||
| type: "select", | |||||
| required: false, | |||||
| options: [ | |||||
| { label: "全部", value: "All" }, | |||||
| { label: "PP", value: "PP" }, | |||||
| { label: "PF", value: "PF" }, | |||||
| ], | |||||
| }, | |||||
| ] | ] | ||||
| }, | }, | ||||
| { | { | ||||
| @@ -193,13 +187,7 @@ export const REPORTS: ReportDefinition[] = [ | |||||
| name: "store_id", | name: "store_id", | ||||
| type: "select", | type: "select", | ||||
| required: false, | required: false, | ||||
| options: [ | |||||
| { label: "全部", value: "All" }, | |||||
| { label: "1F", value: "1F" }, | |||||
| { label: "2F", value: "2F" }, | |||||
| { label: "3F", value: "3F" }, | |||||
| { label: "4F", value: "4F" } | |||||
| ], | |||||
| options: floorOptionsAll, | |||||
| }, | }, | ||||
| { | { | ||||
| label: "狀態", | label: "狀態", | ||||
| @@ -220,14 +208,11 @@ export const REPORTS: ReportDefinition[] = [ | |||||
| multiple: true, | multiple: true, | ||||
| options: [ | options: [ | ||||
| { label: "全部", value: "All" }, | { label: "全部", value: "All" }, | ||||
| { label: "PP", value: "PP" }, | |||||
| { label: "PF", value: "PF" }, | |||||
| { label: "TOA", value: "TOA" }, | |||||
| { label: "工廠生產", value: "工廠生產" }, | |||||
| { label: "倉存調整", value: "倉存調整" }, | |||||
| { label: "期初存貨", value: "期初存貨" }, | |||||
| { label: "採購入倉", value: "採購入倉" }, | |||||
| { label: "其他入倉", value: "其他入倉" }, | |||||
| { label: "採購", value: "purchase" }, | |||||
| { label: "生產", value: "production" }, | |||||
| { label: "倉存調整", value: "adjustment" }, | |||||
| { label: "期初存貨", value: "opening" }, | |||||
| { label: "其他", value: "other" }, | |||||
| ], | ], | ||||
| }, | }, | ||||
| ] | ] | ||||
| @@ -241,19 +226,13 @@ export const REPORTS: ReportDefinition[] = [ | |||||
| fields: [ | fields: [ | ||||
| { label: "庫存日期 Stock Date(僅今天)", name: "stockDate", type: "date", required: true, disabled: true }, | { label: "庫存日期 Stock Date(僅今天)", name: "stockDate", type: "date", required: true, disabled: true }, | ||||
| { label: "貨品編號 Item Code", name: "itemCode", type: "text", required: false }, | { label: "貨品編號 Item Code", name: "itemCode", type: "text", required: false }, | ||||
| { label: "倉位 Warehouse Code", name: "warehouseCode", type: "text", required: false, placeholder: "e.g. W200 or 2F-W200-#A-00" }, | |||||
| { label: "倉位 Warehouse Code", name: "warehouseCode", type: "text", required: false, placeholder: "e.g. DEMO-A" }, | |||||
| { | { | ||||
| label: "樓層 Store ID", | label: "樓層 Store ID", | ||||
| name: "storeId", | name: "storeId", | ||||
| type: "select", | type: "select", | ||||
| required: false, | required: false, | ||||
| options: [ | |||||
| { label: "全部", value: "All" }, | |||||
| { label: "1F", value: "1F" }, | |||||
| { label: "2F", value: "2F" }, | |||||
| { label: "3F", value: "3F" }, | |||||
| { label: "4F", value: "4F" }, | |||||
| ], | |||||
| options: floorOptionsAll, | |||||
| }, | }, | ||||
| { label: "批號 Lot No", name: "lotNo", type: "text", required: false, placeholder: "e.g. LT-202608" }, | { label: "批號 Lot No", name: "lotNo", type: "text", required: false, placeholder: "e.g. LT-202608" }, | ||||
| ], | ], | ||||
| @@ -271,15 +250,9 @@ export const REPORTS: ReportDefinition[] = [ | |||||
| name: "storeId", | name: "storeId", | ||||
| type: "select", | type: "select", | ||||
| required: false, | required: false, | ||||
| options: [ | |||||
| { label: "全部", value: "All" }, | |||||
| { label: "1F", value: "1F" }, | |||||
| { label: "2F", value: "2F" }, | |||||
| { label: "3F", value: "3F" }, | |||||
| { label: "4F", value: "4F" }, | |||||
| ], | |||||
| options: floorOptionsAll, | |||||
| }, | }, | ||||
| { label: "倉庫 Warehouse", name: "warehouse", type: "text", required: false, placeholder: "e.g. W201" }, | |||||
| { label: "倉庫 Warehouse", name: "warehouse", type: "text", required: false, placeholder: "e.g. DEMO-A" }, | |||||
| { label: "區域 Area", name: "area", type: "text", required: false, placeholder: "e.g. #A" }, | { label: "區域 Area", name: "area", type: "text", required: false, placeholder: "e.g. #A" }, | ||||
| { label: "儲位 Slot", name: "slot", type: "text", required: false, placeholder: "e.g. 02" }, | { label: "儲位 Slot", name: "slot", type: "text", required: false, placeholder: "e.g. 02" }, | ||||
| { | { | ||||
| @@ -299,8 +272,8 @@ export const REPORTS: ReportDefinition[] = [ | |||||
| required: false, | required: false, | ||||
| options: [ | options: [ | ||||
| { label: "全部", value: "All" }, | { label: "全部", value: "All" }, | ||||
| { label: "PP", value: "PP" }, | |||||
| { label: "PF", value: "PF" }, | |||||
| { label: "採購", value: "purchase" }, | |||||
| { label: "生產", value: "production" }, | |||||
| { label: "其他", value: "other" }, | { label: "其他", value: "other" }, | ||||
| ], | ], | ||||
| }, | }, | ||||
| @@ -442,12 +415,10 @@ export const REPORTS: ReportDefinition[] = [ | |||||
| { label: "類別 Category", name: "stockCategory", type: "select", required: false, | { label: "類別 Category", name: "stockCategory", type: "select", required: false, | ||||
| multiple: true, | multiple: true, | ||||
| options: [ | options: [ | ||||
| { label: "All", value: "All" }, | |||||
| { label: "MAT", value: "MAT" }, | |||||
| { label: "WIP", value: "WIP" }, | |||||
| { label: "NM", value: "NM" }, | |||||
| { label: "FG", value: "FG" }, | |||||
| { label: "CMB", value: "CMB" } | |||||
| { label: "全部", value: "All" }, | |||||
| { label: "原料", value: "mat" }, | |||||
| { label: "半成品", value: "sfg" }, | |||||
| { label: "成品", value: "fg" }, | |||||
| ] }, | ] }, | ||||
| { label: "貨品編號 Item Code", name: "itemCode", type: "select", required: false, | { label: "貨品編號 Item Code", name: "itemCode", type: "select", required: false, | ||||
| multiple: true, | multiple: true, | ||||
| @@ -472,9 +443,9 @@ export const REPORTS: ReportDefinition[] = [ | |||||
| { label: "類別 Category", name: "stockCategory", type: "select", required: false, | { label: "類別 Category", name: "stockCategory", type: "select", required: false, | ||||
| multiple: true, | multiple: true, | ||||
| options: [ | options: [ | ||||
| { label: "All", value: "All" }, | |||||
| { label: "WIP", value: "WIP" }, | |||||
| { label: "FG", value: "FG" }, | |||||
| { label: "全部", value: "All" }, | |||||
| { label: "半成品", value: "sfg" }, | |||||
| { label: "成品", value: "fg" }, | |||||
| ] }, | ] }, | ||||
| { label: "貨品編號 Item Code", name: "itemCode", type: "select", required: false, | { label: "貨品編號 Item Code", name: "itemCode", type: "select", required: false, | ||||
| multiple: true, | multiple: true, | ||||
| @@ -533,10 +504,7 @@ export const REPORTS: ReportDefinition[] = [ | |||||
| name: "storeId", | name: "storeId", | ||||
| type: "select", | type: "select", | ||||
| required: false, | required: false, | ||||
| options: [ | |||||
| { label: "2F", value: "2F" }, | |||||
| { label: "4F", value: "4F" }, | |||||
| ], | |||||
| options: floorOptions, | |||||
| }, | }, | ||||
| ], | ], | ||||
| }, | }, | ||||
| @@ -574,11 +542,7 @@ export const REPORTS: ReportDefinition[] = [ | |||||
| name: "storeId", | name: "storeId", | ||||
| type: "select", | type: "select", | ||||
| required: false, | required: false, | ||||
| options: [ | |||||
| { label: "全部", value: "All" }, | |||||
| { label: "2F", value: "2F" }, | |||||
| { label: "4F", value: "4F" }, | |||||
| ], | |||||
| options: floorOptionsAll, | |||||
| }, | }, | ||||
| ], | ], | ||||
| }, | }, | ||||
| @@ -1,12 +1,12 @@ | |||||
| { | { | ||||
| "title": "Delivery order / workbench floor (supplier codes)", | "title": "Delivery order / workbench floor (supplier codes)", | ||||
| "Intro": "Manage supplier codes assigned to each floor. Use the edit button to add or remove suppliers from a floor.", | "Intro": "Manage supplier codes assigned to each floor. Use the edit button to add or remove suppliers from a floor.", | ||||
| "2F supplier": "2F supplier", | |||||
| "4F supplier": "4F supplier", | |||||
| "Edit 2F": "Edit 2F", | |||||
| "Edit 4F": "Edit 4F", | |||||
| "Edit 2F title": "Edit 2F supplier codes", | |||||
| "Edit 4F title": "Edit 4F supplier codes", | |||||
| "2F supplier": "2/F supplier", | |||||
| "4F supplier": "4/F supplier", | |||||
| "Edit 2F": "Edit 2/F", | |||||
| "Edit 4F": "Edit 4/F", | |||||
| "Edit 2F title": "Edit 2/F supplier codes", | |||||
| "Edit 4F title": "Edit 4/F supplier codes", | |||||
| "Edit dialog title": "Edit floor–supplier mapping", | "Edit dialog title": "Edit floor–supplier mapping", | ||||
| "Floor label": "Floor", | "Floor label": "Floor", | ||||
| "Add mapping": "Add mapping", | "Add mapping": "Add mapping", | ||||
| @@ -20,10 +20,10 @@ | |||||
| "btnSyncDoExtra": "Sync DO (Extra) from M18", | "btnSyncDoExtra": "Sync DO (Extra) from M18", | ||||
| "btnSyncProduct": "Sync product from M18", | "btnSyncProduct": "Sync product from M18", | ||||
| "syncing": "Syncing...", | "syncing": "Syncing...", | ||||
| "placeholderPoCode": "e.g. PFP002PO26030341", | |||||
| "placeholderPoCode": "e.g. DEMO-PO-001", | |||||
| "placeholderDoCode": "e.g. same document code as M18 shop PO", | "placeholderDoCode": "e.g. same document code as M18 shop PO", | ||||
| "placeholderDoExtraCode": "Enter multiple codes separated by comma or newline", | "placeholderDoExtraCode": "Enter multiple codes separated by comma or newline", | ||||
| "placeholderProductCode": "e.g. PP1175 (M18 item code)", | |||||
| "placeholderProductCode": "e.g. DEMO-FG-01", | |||||
| "waiting": "Waiting for implementation...", | "waiting": "Waiting for implementation...", | ||||
| "noResponse": "No response received", | "noResponse": "No response received", | ||||
| "alreadyExists": "{{docName}} already exists in system", | "alreadyExists": "{{docName}} already exists in system", | ||||
| @@ -57,17 +57,11 @@ | |||||
| "warehouse": "Warehouse", | "warehouse": "Warehouse", | ||||
| "area": "Area", | "area": "Area", | ||||
| "slot": "Slot", | "slot": "Slot", | ||||
| "lotNo": "Lot No", | |||||
| "poPrefix": "PP/PF" | |||||
| "lotNo": "Lot No" | |||||
| }, | }, | ||||
| "options": { | "options": { | ||||
| "storeId": { | "storeId": { | ||||
| "All": "All" | "All": "All" | ||||
| }, | |||||
| "poPrefix": { | |||||
| "All": "All", | |||||
| "PP": "PP", | |||||
| "PF": "PF" | |||||
| } | } | ||||
| }, | }, | ||||
| "fieldHints": { | "fieldHints": { | ||||
| @@ -107,14 +101,11 @@ | |||||
| }, | }, | ||||
| "type": { | "type": { | ||||
| "All": "All", | "All": "All", | ||||
| "PP": "PP", | |||||
| "PF": "PF", | |||||
| "TOA": "TOA", | |||||
| "工廠生產": "Factory production", | |||||
| "倉存調整": "Inventory adjustment", | |||||
| "期初存貨": "Opening inventory", | |||||
| "採購入倉": "Purchase inbound", | |||||
| "其他入倉": "Other inbound" | |||||
| "purchase": "Purchase", | |||||
| "production": "Production", | |||||
| "adjustment": "Inventory adjustment", | |||||
| "opening": "Opening inventory", | |||||
| "other": "Other" | |||||
| } | } | ||||
| } | } | ||||
| }, | }, | ||||
| @@ -208,6 +199,14 @@ | |||||
| }, | }, | ||||
| "fieldHints": { | "fieldHints": { | ||||
| "itemCodePaste": "Paste item codes from Excel. Separate with spaces, commas, or new lines" | "itemCodePaste": "Paste item codes from Excel. Separate with spaces, commas, or new lines" | ||||
| }, | |||||
| "options": { | |||||
| "stockCategory": { | |||||
| "All": "All", | |||||
| "mat": "Material", | |||||
| "sfg": "Semi-finished", | |||||
| "fg": "Finished goods" | |||||
| } | |||||
| } | } | ||||
| }, | }, | ||||
| "rep-005": { | "rep-005": { | ||||
| @@ -222,6 +221,13 @@ | |||||
| }, | }, | ||||
| "fieldHints": { | "fieldHints": { | ||||
| "itemCodePaste": "Paste item codes from Excel. Separate with spaces, commas, or new lines" | "itemCodePaste": "Paste item codes from Excel. Separate with spaces, commas, or new lines" | ||||
| }, | |||||
| "options": { | |||||
| "stockCategory": { | |||||
| "All": "All", | |||||
| "sfg": "Semi-finished", | |||||
| "fg": "Finished goods" | |||||
| } | |||||
| } | } | ||||
| }, | }, | ||||
| "rep-015": { | "rep-015": { | ||||
| @@ -295,8 +301,8 @@ | |||||
| }, | }, | ||||
| "lotOrigin": { | "lotOrigin": { | ||||
| "All": "All", | "All": "All", | ||||
| "PP": "PP", | |||||
| "PF": "PF", | |||||
| "purchase": "Purchase", | |||||
| "production": "Production", | |||||
| "other": "Other" | "other": "Other" | ||||
| } | } | ||||
| } | } | ||||
| @@ -1,12 +1,12 @@ | |||||
| { | { | ||||
| "title": "送貨單樓層設定(供應商代碼)", | "title": "送貨單樓層設定(供應商代碼)", | ||||
| "Intro": "管理各樓層的供應商代碼。點擊編輯按鈕可新增或移除供應商。", | "Intro": "管理各樓層的供應商代碼。點擊編輯按鈕可新增或移除供應商。", | ||||
| "2F supplier": "2F 供應商", | |||||
| "4F supplier": "4F 供應商", | |||||
| "Edit 2F": "編輯 2F", | |||||
| "Edit 4F": "編輯 4F", | |||||
| "Edit 2F title": "編輯 2F 供應商代碼", | |||||
| "Edit 4F title": "編輯 4F 供應商代碼", | |||||
| "2F supplier": "2/F 供應商", | |||||
| "4F supplier": "4/F 供應商", | |||||
| "Edit 2F": "編輯 2/F", | |||||
| "Edit 4F": "編輯 4/F", | |||||
| "Edit 2F title": "編輯 2/F 供應商代碼", | |||||
| "Edit 4F title": "編輯 4/F 供應商代碼", | |||||
| "Edit dialog title": "編輯樓層供應商映射", | "Edit dialog title": "編輯樓層供應商映射", | ||||
| "Floor label": "樓層", | "Floor label": "樓層", | ||||
| "Add mapping": "新增映射", | "Add mapping": "新增映射", | ||||
| @@ -1,3 +1,3 @@ | |||||
| { | { | ||||
| "title": "檸檬機(激光機)" | |||||
| "title": "激光機" | |||||
| } | } | ||||
| @@ -20,10 +20,10 @@ | |||||
| "btnSyncDoExtra": "從 M18 同步送貨訂單(加單)", | "btnSyncDoExtra": "從 M18 同步送貨訂單(加單)", | ||||
| "btnSyncProduct": "從 M18 同步貨品", | "btnSyncProduct": "從 M18 同步貨品", | ||||
| "syncing": "同步中…", | "syncing": "同步中…", | ||||
| "placeholderPoCode": "例:PFP002PO26030341", | |||||
| "placeholderPoCode": "例:DEMO-PO-001", | |||||
| "placeholderDoCode": "例:與 M18 店鋪訂單相同的文件編號", | "placeholderDoCode": "例:與 M18 店鋪訂單相同的文件編號", | ||||
| "placeholderDoExtraCode": "可輸入多個 code,用逗號或換行分隔", | "placeholderDoExtraCode": "可輸入多個 code,用逗號或換行分隔", | ||||
| "placeholderProductCode": "例:PP1175(M18 貨品編號)", | |||||
| "placeholderProductCode": "例:DEMO-FG-01", | |||||
| "waiting": "等待實作…", | "waiting": "等待實作…", | ||||
| "noResponse": "未收到回應", | "noResponse": "未收到回應", | ||||
| "alreadyExists": "{{docName}}已存在系統", | "alreadyExists": "{{docName}}已存在系統", | ||||
| @@ -25,7 +25,7 @@ | |||||
| "nav.breadcrumb.joEdit": "工單詳情", | "nav.breadcrumb.joEdit": "工單詳情", | ||||
| "nav.breadcrumb.joTesting": "工單測試", | "nav.breadcrumb.joTesting": "工單測試", | ||||
| "nav.breadcrumb.joWorkbench": "工單工作台", | "nav.breadcrumb.joWorkbench": "工單工作台", | ||||
| "nav.breadcrumb.laserPrint": "檸檬機(激光機)", | |||||
| "nav.breadcrumb.laserPrint": "激光機", | |||||
| "nav.breadcrumb.m18Sync": "M18 同步", | "nav.breadcrumb.m18Sync": "M18 同步", | ||||
| "nav.breadcrumb.poEdit": "編輯", | "nav.breadcrumb.poEdit": "編輯", | ||||
| "nav.breadcrumb.poWorkbench": "採購單工作台", | "nav.breadcrumb.poWorkbench": "採購單工作台", | ||||
| @@ -64,7 +64,7 @@ | |||||
| "nav.jobOrder.productionProcess": "工單生產流程", | "nav.jobOrder.productionProcess": "工單生產流程", | ||||
| "nav.jobOrder.searchCreate": "搜索工單/ 建立工單", | "nav.jobOrder.searchCreate": "搜索工單/ 建立工單", | ||||
| "nav.jobOrderManagement": "管理工單", | "nav.jobOrderManagement": "管理工單", | ||||
| "nav.laserPrint": "檸檬機(激光機)", | |||||
| "nav.laserPrint": "激光機", | |||||
| "nav.m18Sync": "M18 同步", | "nav.m18Sync": "M18 同步", | ||||
| "nav.productionServer": "正式伺服器", | "nav.productionServer": "正式伺服器", | ||||
| "nav.report": "報告管理", | "nav.report": "報告管理", | ||||
| @@ -57,17 +57,11 @@ | |||||
| "warehouse": "倉庫", | "warehouse": "倉庫", | ||||
| "area": "區域", | "area": "區域", | ||||
| "slot": "儲位", | "slot": "儲位", | ||||
| "lotNo": "批號", | |||||
| "poPrefix": "PP/PF 分類" | |||||
| "lotNo": "批號" | |||||
| }, | }, | ||||
| "options": { | "options": { | ||||
| "storeId": { | "storeId": { | ||||
| "All": "全部" | "All": "全部" | ||||
| }, | |||||
| "poPrefix": { | |||||
| "All": "全部", | |||||
| "PP": "PP", | |||||
| "PF": "PF" | |||||
| } | } | ||||
| }, | }, | ||||
| "fieldHints": { | "fieldHints": { | ||||
| @@ -107,14 +101,11 @@ | |||||
| }, | }, | ||||
| "type": { | "type": { | ||||
| "All": "全部", | "All": "全部", | ||||
| "PP": "PP", | |||||
| "PF": "PF", | |||||
| "TOA": "TOA", | |||||
| "工廠生產": "工廠生產", | |||||
| "倉存調整": "倉存調整", | |||||
| "期初存貨": "期初存貨", | |||||
| "採購入倉": "採購入倉", | |||||
| "其他入倉": "其他入倉" | |||||
| "purchase": "採購", | |||||
| "production": "生產", | |||||
| "adjustment": "倉存調整", | |||||
| "opening": "期初存貨", | |||||
| "other": "其他" | |||||
| } | } | ||||
| } | } | ||||
| }, | }, | ||||
| @@ -208,6 +199,14 @@ | |||||
| }, | }, | ||||
| "fieldHints": { | "fieldHints": { | ||||
| "itemCodePaste": "可從 Excel 貼上多個貨品編號,以空格、逗號或換行分隔" | "itemCodePaste": "可從 Excel 貼上多個貨品編號,以空格、逗號或換行分隔" | ||||
| }, | |||||
| "options": { | |||||
| "stockCategory": { | |||||
| "All": "全部", | |||||
| "mat": "原料", | |||||
| "sfg": "半成品", | |||||
| "fg": "成品" | |||||
| } | |||||
| } | } | ||||
| }, | }, | ||||
| "rep-005": { | "rep-005": { | ||||
| @@ -222,6 +221,13 @@ | |||||
| }, | }, | ||||
| "fieldHints": { | "fieldHints": { | ||||
| "itemCodePaste": "可從 Excel 貼上多個貨品編號,以空格、逗號或換行分隔" | "itemCodePaste": "可從 Excel 貼上多個貨品編號,以空格、逗號或換行分隔" | ||||
| }, | |||||
| "options": { | |||||
| "stockCategory": { | |||||
| "All": "全部", | |||||
| "sfg": "半成品", | |||||
| "fg": "成品" | |||||
| } | |||||
| } | } | ||||
| }, | }, | ||||
| "rep-015": { | "rep-015": { | ||||
| @@ -295,8 +301,8 @@ | |||||
| }, | }, | ||||
| "lotOrigin": { | "lotOrigin": { | ||||
| "All": "全部", | "All": "全部", | ||||
| "PP": "PP", | |||||
| "PF": "PF", | |||||
| "purchase": "採購", | |||||
| "production": "生產", | |||||
| "other": "其他" | "other": "其他" | ||||
| } | } | ||||
| } | } | ||||