FPSMS-frontend
Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.
 
 

199 linhas
6.8 KiB

  1. "use client";
  2. import { NEXT_PUBLIC_API_URL } from '@/config/api';
  3. import { clientAuthFetch } from '@/app/utils/clientAuthFetch';
  4. import { buildItemCodePasteRequestBody } from './parseItemCodeTokens';
  5. export interface ItemCodeWithName {
  6. code: string;
  7. name: string;
  8. }
  9. export interface ItemCodeWithCategory {
  10. code: string;
  11. category: string;
  12. name?: string;
  13. }
  14. /**
  15. * Fetch item codes for SemiFG Production Analysis Report
  16. * @param stockCategory - Comma-separated stock categories (e.g., "FG,WIP") or empty string for all
  17. * @returns Array of item codes with names
  18. */
  19. export const fetchSemiFGItemCodes = async (
  20. stockCategory: string = ''
  21. ): Promise<ItemCodeWithName[]> => {
  22. let url = `${NEXT_PUBLIC_API_URL}/report/semi-fg-item-codes`;
  23. if (stockCategory && stockCategory !== 'All' && !stockCategory.includes('All')) {
  24. url = `${url}?stockCategory=${stockCategory}`;
  25. }
  26. const response = await clientAuthFetch(url, {
  27. method: 'GET',
  28. headers: { 'Content-Type': 'application/json' },
  29. });
  30. if (response.status === 401 || response.status === 403) throw new Error("Unauthorized");
  31. if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
  32. return await response.json();
  33. };
  34. /**
  35. * Fetch item codes with category information for SemiFG Production Analysis Report
  36. * @param stockCategory - Comma-separated stock categories (e.g., "FG,WIP") or empty string for all
  37. * @returns Array of item codes with category and name
  38. */
  39. export const fetchSemiFGItemCodesWithCategory = async (
  40. stockCategory: string = ''
  41. ): Promise<ItemCodeWithCategory[]> => {
  42. let url = `${NEXT_PUBLIC_API_URL}/report/semi-fg-item-codes-with-category`;
  43. if (stockCategory && stockCategory !== 'All' && !stockCategory.includes('All')) {
  44. url = `${url}?stockCategory=${stockCategory}`;
  45. }
  46. const response = await clientAuthFetch(url, {
  47. method: 'GET',
  48. headers: { 'Content-Type': 'application/json' },
  49. });
  50. if (response.status === 401 || response.status === 403) throw new Error("Unauthorized");
  51. if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
  52. return await response.json();
  53. };
  54. /**
  55. * Generate and download the SemiFG Production Analysis Report PDF
  56. * @param criteria - Report criteria parameters
  57. * @param reportTitle - Title of the report for filename
  58. * @returns Promise that resolves when download is complete
  59. */
  60. export const generateSemiFGProductionAnalysisReport = async (
  61. criteria: Record<string, string>,
  62. reportTitle: string = '成品/半成品生產分析報告'
  63. ): Promise<void> => {
  64. const url = `${NEXT_PUBLIC_API_URL}/report/print-semi-fg-production-analysis`;
  65. const response = await clientAuthFetch(url, {
  66. method: 'POST',
  67. headers: {
  68. Accept: 'application/pdf',
  69. 'Content-Type': 'application/json',
  70. },
  71. body: JSON.stringify(buildItemCodePasteRequestBody(criteria, 'itemCode')),
  72. });
  73. if (response.status === 401 || response.status === 403) throw new Error("Unauthorized");
  74. if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
  75. const blob = await response.blob();
  76. const downloadUrl = window.URL.createObjectURL(blob);
  77. const link = document.createElement('a');
  78. link.href = downloadUrl;
  79. const contentDisposition = response.headers.get('Content-Disposition');
  80. let fileName = `${reportTitle}.pdf`;
  81. if (contentDisposition?.includes('filename=')) {
  82. fileName = contentDisposition.split('filename=')[1].split(';')[0].replace(/"/g, '');
  83. }
  84. link.setAttribute('download', fileName);
  85. document.body.appendChild(link);
  86. link.click();
  87. link.remove();
  88. window.URL.revokeObjectURL(downloadUrl);
  89. };
  90. /**
  91. * Generate and download the SemiFG Production Analysis Report as Excel
  92. * @param criteria - Report criteria parameters
  93. * @param reportTitle - Title of the report for filename
  94. * @returns Promise that resolves when download is complete
  95. */
  96. export const generateSemiFGProductionAnalysisReportExcel = async (
  97. criteria: Record<string, string>,
  98. reportTitle: string = '成品/半成品生產分析報告'
  99. ): Promise<void> => {
  100. const url = `${NEXT_PUBLIC_API_URL}/report/print-semi-fg-production-analysis-excel`;
  101. const response = await clientAuthFetch(url, {
  102. method: 'POST',
  103. headers: {
  104. Accept: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
  105. 'Content-Type': 'application/json',
  106. },
  107. body: JSON.stringify(buildItemCodePasteRequestBody(criteria, 'itemCode')),
  108. });
  109. if (response.status === 401 || response.status === 403) throw new Error('Unauthorized');
  110. if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
  111. const blob = await response.blob();
  112. const downloadUrl = window.URL.createObjectURL(blob);
  113. const link = document.createElement('a');
  114. link.href = downloadUrl;
  115. const contentDisposition = response.headers.get('Content-Disposition');
  116. let fileName = `${reportTitle}.xlsx`;
  117. if (contentDisposition?.includes('filename=')) {
  118. fileName = contentDisposition.split('filename=')[1].split(';')[0].replace(/"/g, '');
  119. }
  120. link.setAttribute('download', fileName);
  121. document.body.appendChild(link);
  122. link.click();
  123. link.remove();
  124. window.URL.revokeObjectURL(downloadUrl);
  125. };
  126. /**
  127. * Excel with one column per day in the selected production date range.
  128. */
  129. export const generateSemiFGProductionAnalysisReportExcelDaily = async (
  130. criteria: Record<string, string>,
  131. reportTitle: string = '成品/半成品生產分析報告'
  132. ): Promise<void> => {
  133. const url = `${NEXT_PUBLIC_API_URL}/report/print-semi-fg-production-analysis-excel-daily`;
  134. const response = await clientAuthFetch(url, {
  135. method: 'POST',
  136. headers: {
  137. Accept: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
  138. 'Content-Type': 'application/json',
  139. },
  140. body: JSON.stringify(buildItemCodePasteRequestBody(criteria, 'itemCode')),
  141. });
  142. if (response.status === 401 || response.status === 403) throw new Error('Unauthorized');
  143. if (!response.ok) throw new Error(await readReportErrorMessage(response));
  144. const blob = await response.blob();
  145. const downloadUrl = window.URL.createObjectURL(blob);
  146. const link = document.createElement('a');
  147. link.href = downloadUrl;
  148. const contentDisposition = response.headers.get('Content-Disposition');
  149. let fileName = `${reportTitle}_每日.xlsx`;
  150. if (contentDisposition?.includes('filename=')) {
  151. fileName = contentDisposition.split('filename=')[1].split(';')[0].replace(/"/g, '');
  152. }
  153. link.setAttribute('download', fileName);
  154. document.body.appendChild(link);
  155. link.click();
  156. link.remove();
  157. window.URL.revokeObjectURL(downloadUrl);
  158. };
  159. async function readReportErrorMessage(response: Response): Promise<string> {
  160. const text = await response.text();
  161. try {
  162. const parsed = JSON.parse(text) as { message?: string };
  163. if (parsed.message) return parsed.message;
  164. } catch {
  165. // Response body is not JSON.
  166. }
  167. return text.trim();
  168. }