|
|
@@ -26,6 +26,7 @@ import { |
|
|
import DownloadIcon from '@mui/icons-material/Download'; |
|
|
import DownloadIcon from '@mui/icons-material/Download'; |
|
|
import InfoOutlinedIcon from '@mui/icons-material/InfoOutlined'; |
|
|
import InfoOutlinedIcon from '@mui/icons-material/InfoOutlined'; |
|
|
import { REPORTS } from '@/config/reportConfig'; |
|
|
import { REPORTS } from '@/config/reportConfig'; |
|
|
|
|
|
import { mergePastedItemCodes, buildStockBalanceRequestBody } from './parseItemCodeTokens'; |
|
|
import { NEXT_PUBLIC_API_URL } from '@/config/api'; |
|
|
import { NEXT_PUBLIC_API_URL } from '@/config/api'; |
|
|
import { clientAuthFetch } from '@/app/utils/clientAuthFetch'; |
|
|
import { clientAuthFetch } from '@/app/utils/clientAuthFetch'; |
|
|
import SemiFGProductionAnalysisReport from './SemiFGProductionAnalysisReport'; |
|
|
import SemiFGProductionAnalysisReport from './SemiFGProductionAnalysisReport'; |
|
|
@@ -80,7 +81,7 @@ const FIELD_ERROR_SX = { |
|
|
/** FP-MTMS Version Checklist | Functions Ref. No. 82 | v1.0.0 | 2026-09-10 */ |
|
|
/** FP-MTMS Version Checklist | Functions Ref. No. 82 | v1.0.0 | 2026-09-10 */ |
|
|
export default function ReportPage() { |
|
|
export default function ReportPage() { |
|
|
const { data: session } = useSession() as { data: SessionWithTokens | null }; |
|
|
const { data: session } = useSession() as { data: SessionWithTokens | null }; |
|
|
const { t, i18n, reportTitle, fieldLabel, optionLabel } = useReportLabels(); |
|
|
|
|
|
|
|
|
const { t, i18n, reportTitle, fieldLabel, fieldHint, optionLabel } = useReportLabels(); |
|
|
const isZh = (i18n.language || 'zh').startsWith('zh'); |
|
|
const isZh = (i18n.language || 'zh').startsWith('zh'); |
|
|
const dateDisplayFormat = isZh ? 'DD/MM/YYYY' : 'DD/MM/YYYY'; |
|
|
const dateDisplayFormat = isZh ? 'DD/MM/YYYY' : 'DD/MM/YYYY'; |
|
|
const includeGrnFinancialColumns = |
|
|
const includeGrnFinancialColumns = |
|
|
@@ -358,6 +359,22 @@ export default function ReportPage() { |
|
|
return p.toString(); |
|
|
return p.toString(); |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
const buildCriteriaQueryString = (): string => { |
|
|
|
|
|
if (!currentReport) return ''; |
|
|
|
|
|
if (currentReport.id === 'rep-012') return buildRep012QueryString(); |
|
|
|
|
|
if (currentReport.id === 'rep-010') return buildRep010QueryString(); |
|
|
|
|
|
const merged = mergePastedItemCodes(criteria); |
|
|
|
|
|
const p = new URLSearchParams(merged); |
|
|
|
|
|
if (currentReport.id === 'rep-016') { |
|
|
|
|
|
const day = (merged.dateStart || '').trim(); |
|
|
|
|
|
if (day) { |
|
|
|
|
|
p.set('dateStart', day); |
|
|
|
|
|
p.set('dateEnd', day); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
return p.toString(); |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
const handlePrint = async () => { |
|
|
const handlePrint = async () => { |
|
|
if (!currentReport) return; |
|
|
if (!currentReport) return; |
|
|
if (!validateRequiredFields()) return; |
|
|
if (!validateRequiredFields()) return; |
|
|
@@ -399,28 +416,24 @@ export default function ReportPage() { |
|
|
await generateShopOrderReplenishmentReportExcel(criteria, reportTitle(currentReport), t); |
|
|
await generateShopOrderReplenishmentReportExcel(criteria, reportTitle(currentReport), t); |
|
|
} else { |
|
|
} else { |
|
|
// Backend returns actual .xlsx bytes for this Excel endpoint. |
|
|
// Backend returns actual .xlsx bytes for this Excel endpoint. |
|
|
let queryParams = |
|
|
|
|
|
currentReport.id === 'rep-012' |
|
|
|
|
|
? buildRep012QueryString() |
|
|
|
|
|
: currentReport.id === 'rep-010' |
|
|
|
|
|
? buildRep010QueryString() |
|
|
|
|
|
: new URLSearchParams(criteria).toString(); |
|
|
|
|
|
// rep-016: single-day UI — mirror dateStart to dateEnd for backend API. |
|
|
|
|
|
if (currentReport.id === 'rep-016') { |
|
|
|
|
|
const p = new URLSearchParams(criteria); |
|
|
|
|
|
const day = (criteria.dateStart || '').trim(); |
|
|
|
|
|
if (day) { |
|
|
|
|
|
p.set('dateStart', day); |
|
|
|
|
|
p.set('dateEnd', day); |
|
|
|
|
|
} |
|
|
|
|
|
queryParams = p.toString(); |
|
|
|
|
|
} |
|
|
|
|
|
const excelUrl = `${currentReport.apiEndpoint}-excel?${queryParams}`; |
|
|
|
|
|
|
|
|
|
|
|
const response = await clientAuthFetch(excelUrl, { |
|
|
|
|
|
method: 'GET', |
|
|
|
|
|
headers: { Accept: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }, |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
const isStockBalance = currentReport.id === 'rep-007'; |
|
|
|
|
|
const excelUrl = isStockBalance |
|
|
|
|
|
? `${currentReport.apiEndpoint}-excel` |
|
|
|
|
|
: `${currentReport.apiEndpoint}-excel?${buildCriteriaQueryString()}`; |
|
|
|
|
|
|
|
|
|
|
|
const response = await clientAuthFetch(excelUrl, isStockBalance |
|
|
|
|
|
? { |
|
|
|
|
|
method: 'POST', |
|
|
|
|
|
headers: { |
|
|
|
|
|
Accept: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', |
|
|
|
|
|
'Content-Type': 'application/json', |
|
|
|
|
|
}, |
|
|
|
|
|
body: JSON.stringify(buildStockBalanceRequestBody(criteria)), |
|
|
|
|
|
} |
|
|
|
|
|
: { |
|
|
|
|
|
method: 'GET', |
|
|
|
|
|
headers: { Accept: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }, |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
if (response.status === 401 || response.status === 403) return; |
|
|
if (response.status === 401 || response.status === 403) return; |
|
|
if (response.status === 204) { |
|
|
if (response.status === 204) { |
|
|
@@ -472,18 +485,24 @@ export default function ReportPage() { |
|
|
|
|
|
|
|
|
setLoading(true); |
|
|
setLoading(true); |
|
|
try { |
|
|
try { |
|
|
let queryParams = |
|
|
|
|
|
currentReport.id === 'rep-012' |
|
|
|
|
|
? buildRep012QueryString() |
|
|
|
|
|
: currentReport.id === 'rep-010' |
|
|
|
|
|
? buildRep010QueryString() |
|
|
|
|
|
: new URLSearchParams(criteria).toString(); |
|
|
|
|
|
const url = `${currentReport.apiEndpoint}?${queryParams}`; |
|
|
|
|
|
|
|
|
|
|
|
const response = await clientAuthFetch(url, { |
|
|
|
|
|
method: 'GET', |
|
|
|
|
|
headers: { 'Accept': 'application/pdf' }, |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
const isStockBalance = currentReport.id === 'rep-007'; |
|
|
|
|
|
const url = isStockBalance |
|
|
|
|
|
? currentReport.apiEndpoint |
|
|
|
|
|
: `${currentReport.apiEndpoint}?${buildCriteriaQueryString()}`; |
|
|
|
|
|
|
|
|
|
|
|
const response = await clientAuthFetch(url, isStockBalance |
|
|
|
|
|
? { |
|
|
|
|
|
method: 'POST', |
|
|
|
|
|
headers: { |
|
|
|
|
|
Accept: 'application/pdf', |
|
|
|
|
|
'Content-Type': 'application/json', |
|
|
|
|
|
}, |
|
|
|
|
|
body: JSON.stringify(buildStockBalanceRequestBody(criteria)), |
|
|
|
|
|
} |
|
|
|
|
|
: { |
|
|
|
|
|
method: 'GET', |
|
|
|
|
|
headers: { Accept: 'application/pdf' }, |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
if (response.status === 401 || response.status === 403) return; |
|
|
if (response.status === 401 || response.status === 403) return; |
|
|
if (!response.ok) { |
|
|
if (!response.ok) { |
|
|
@@ -574,7 +593,10 @@ export default function ReportPage() { |
|
|
: currentValue; |
|
|
: currentValue; |
|
|
|
|
|
|
|
|
// Use larger grid size for 成品/半成品生產分析報告 |
|
|
// Use larger grid size for 成品/半成品生產分析報告 |
|
|
const gridSize = currentReport.id === 'rep-005' ? { xs: 12, sm: 12, md: 6 } : { xs: 12, sm: 6 }; |
|
|
|
|
|
|
|
|
const gridSize = field.multiline |
|
|
|
|
|
? { xs: 12 } |
|
|
|
|
|
: currentReport.id === 'rep-005' ? { xs: 12, sm: 12, md: 6 } : { xs: 12, sm: 6 }; |
|
|
|
|
|
const hintText = fieldHint(currentReport.id, field.name); |
|
|
|
|
|
|
|
|
const disabledByCheckedCheckbox = currentReport.fields.some((f) => { |
|
|
const disabledByCheckedCheckbox = currentReport.fields.some((f) => { |
|
|
if (f.type !== 'checkbox' || criteria[f.name] !== 'true') return false; |
|
|
if (f.type !== 'checkbox' || criteria[f.name] !== 'true') return false; |
|
|
@@ -762,9 +784,11 @@ export default function ReportPage() { |
|
|
fullWidth |
|
|
fullWidth |
|
|
required={field.required} |
|
|
required={field.required} |
|
|
error={Boolean(fieldErrors[field.name])} |
|
|
error={Boolean(fieldErrors[field.name])} |
|
|
helperText={fieldErrors[field.name] || undefined} |
|
|
|
|
|
|
|
|
helperText={fieldErrors[field.name] || hintText || undefined} |
|
|
label={translatedLabel} |
|
|
label={translatedLabel} |
|
|
type={field.type} |
|
|
|
|
|
|
|
|
type={field.multiline ? 'text' : field.type} |
|
|
|
|
|
multiline={field.multiline} |
|
|
|
|
|
minRows={field.multiline ? (field.minRows ?? 4) : undefined} |
|
|
placeholder={field.placeholder} |
|
|
placeholder={field.placeholder} |
|
|
disabled={disabledByCheckedCheckbox || disabledRep012Status || !!field.disabled} |
|
|
disabled={disabledByCheckedCheckbox || disabledRep012Status || !!field.disabled} |
|
|
sx={{ |
|
|
sx={{ |
|
|
|