chore: Fix .env.test quotes and format frontend code

- Fix TEST_ORACLE_USER quotes in .env.test for shell source compatibility
- Format 14 frontend files with Prettier (stores, views, utils)
- All 122 tests passing (77 telegram + 35 backend + 10 E2E)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-22 00:31:20 +02:00
parent bd41a3406e
commit f52aa27bdc
14 changed files with 1691 additions and 1144 deletions

View File

@@ -1,17 +1,17 @@
import * as XLSX from 'xlsx';
import { jsPDF } from 'jspdf';
import autoTable from 'jspdf-autotable';
import * as XLSX from "xlsx";
import { jsPDF } from "jspdf";
import autoTable from "jspdf-autotable";
/**
* Format currency values for export
*/
const formatCurrency = (value) => {
if (value == null || value === '-') return '-';
return new Intl.NumberFormat('ro-RO', {
style: 'currency',
currency: 'RON',
if (value == null || value === "-") return "-";
return new Intl.NumberFormat("ro-RO", {
style: "currency",
currency: "RON",
minimumFractionDigits: 0,
maximumFractionDigits: 0
maximumFractionDigits: 0,
}).format(value);
};
@@ -21,15 +21,18 @@ const formatCurrency = (value) => {
* @param {String} filename - Name of the file (without extension)
* @param {String} sheetName - Name of the Excel sheet
*/
export const exportToExcel = (data, filename, sheetName = 'Sheet1') => {
export const exportToExcel = (data, filename, sheetName = "Sheet1") => {
try {
const ws = XLSX.utils.json_to_sheet(data);
const wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, sheetName);
XLSX.writeFile(wb, `${filename}_${new Date().toISOString().split('T')[0]}.xlsx`);
XLSX.writeFile(
wb,
`${filename}_${new Date().toISOString().split("T")[0]}.xlsx`,
);
return { success: true };
} catch (error) {
console.error('Excel export failed:', error);
console.error("Excel export failed:", error);
return { success: false, error };
}
};
@@ -38,12 +41,12 @@ export const exportToExcel = (data, filename, sheetName = 'Sheet1') => {
* Format number for PDF export
*/
const formatNumberForPDF = (value) => {
if (value == null || value === '' || value === '-') return '-';
if (value == null || value === "" || value === "-") return "-";
const num = parseFloat(value);
if (isNaN(num)) return '-';
return new Intl.NumberFormat('ro-RO', {
if (isNaN(num)) return "-";
return new Intl.NumberFormat("ro-RO", {
minimumFractionDigits: 2,
maximumFractionDigits: 2
maximumFractionDigits: 2,
}).format(num);
};
@@ -58,17 +61,17 @@ export const exportToPDF = (data, columns, filename, header) => {
try {
// Check if data exists
if (!data || data.length === 0) {
console.error('No data to export');
return { success: false, error: 'No data available' };
console.error("No data to export");
return { success: false, error: "No data available" };
}
// Check if jsPDF is properly imported
if (typeof jsPDF === 'undefined') {
console.error('jsPDF not properly imported');
return { success: false, error: 'PDF library not available' };
if (typeof jsPDF === "undefined") {
console.error("jsPDF not properly imported");
return { success: false, error: "PDF library not available" };
}
const doc = new jsPDF('landscape', 'mm', 'a4');
const doc = new jsPDF("landscape", "mm", "a4");
const pageWidth = doc.internal.pageSize.getWidth();
const pageHeight = doc.internal.pageSize.getHeight();
const marginLeft = 8;
@@ -79,38 +82,38 @@ export const exportToPDF = (data, columns, filename, header) => {
const addHeader = () => {
// Line 1: Company name (left aligned, bold, larger font)
doc.setFontSize(13);
doc.setFont(undefined, 'bold');
const companyName = header.companyName || 'N/A';
doc.setFont(undefined, "bold");
const companyName = header.companyName || "N/A";
doc.text(companyName, marginLeft, 15);
// Line 2: Title "Balanta de Verificare" (centered)
doc.setFontSize(14);
doc.setFont(undefined, 'bold');
const titleWidth = doc.getTextWidth(header.title || '');
doc.setFont(undefined, "bold");
const titleWidth = doc.getTextWidth(header.title || "");
const titleX = marginLeft + (contentWidth - titleWidth) / 2;
doc.text(header.title || '', titleX, 24);
doc.text(header.title || "", titleX, 24);
// Line 3: Period (centered, below title)
doc.setFontSize(11);
doc.setFont(undefined, 'normal');
const periodText = header.period || '';
doc.setFont(undefined, "normal");
const periodText = header.period || "";
const periodWidth = doc.getTextWidth(periodText);
const periodX = marginLeft + (contentWidth - periodWidth) / 2;
doc.text(periodText, periodX, 32);
};
// Prepare table data
const tableColumns = columns.map(col => col.header);
const tableRows = data.map(row =>
columns.map(col => {
const tableColumns = columns.map((col) => col.header);
const tableRows = data.map((row) =>
columns.map((col) => {
const value = row[col.field];
if (col.type === 'currency') {
if (col.type === "currency") {
return formatCurrency(value);
} else if (col.type === 'number') {
} else if (col.type === "number") {
return formatNumberForPDF(value);
}
return value || '-';
})
return value || "-";
}),
);
// Function to add footer (called for each page)
@@ -119,8 +122,12 @@ export const exportToPDF = (data, columns, filename, header) => {
// Left side: Generation date
doc.setFontSize(8);
doc.setFont(undefined, 'normal');
doc.text(`Generat: ${new Date().toLocaleString('ro-RO')}`, marginLeft, footerY);
doc.setFont(undefined, "normal");
doc.text(
`Generat: ${new Date().toLocaleString("ro-RO")}`,
marginLeft,
footerY,
);
// Right side: Page numbers
const pageText = `Pagina ${pageNum} din ${totalPages}`;
@@ -129,7 +136,7 @@ export const exportToPDF = (data, columns, filename, header) => {
};
// Check if autoTable is available
if (typeof autoTable === 'function') {
if (typeof autoTable === "function") {
// Build column styles - jspdf-autotable uses numeric keys
const columnStyles = {};
@@ -142,37 +149,37 @@ export const exportToPDF = (data, columns, filename, header) => {
columns.forEach((col, index) => {
// Use custom width if provided, otherwise auto
if (col.width && typeof col.width === 'number') {
if (col.width && typeof col.width === "number") {
widthAllocations[index] = totalWidth * col.width;
} else if (col.width === 'auto') {
widthAllocations[index] = 'auto';
} else if (col.width === "auto") {
widthAllocations[index] = "auto";
} else {
// Default width allocation for Trial Balance (8 columns)
const defaultWidths = {
0: totalWidth * 0.07, // Cont: ~20mm
1: totalWidth * 0.33, // Denumire: ~93mm
2: totalWidth * 0.10, // Sold Prec D: ~28mm
3: totalWidth * 0.10, // Sold Prec C: ~28mm
4: totalWidth * 0.10, // Rulaj D: ~28mm
5: totalWidth * 0.10, // Rulaj C: ~28mm
6: totalWidth * 0.10, // Sold Final D: ~28mm
7: totalWidth * 0.10, // Sold Final C: ~28mm
0: totalWidth * 0.07, // Cont: ~20mm
1: totalWidth * 0.33, // Denumire: ~93mm
2: totalWidth * 0.1, // Sold Prec D: ~28mm
3: totalWidth * 0.1, // Sold Prec C: ~28mm
4: totalWidth * 0.1, // Rulaj D: ~28mm
5: totalWidth * 0.1, // Rulaj C: ~28mm
6: totalWidth * 0.1, // Sold Final D: ~28mm
7: totalWidth * 0.1, // Sold Final C: ~28mm
};
widthAllocations[index] = defaultWidths[index] || 'auto';
widthAllocations[index] = defaultWidths[index] || "auto";
}
});
columns.forEach((col, index) => {
columnStyles[index] = {
cellWidth: widthAllocations[index]
cellWidth: widthAllocations[index],
};
// Set alignment based on type
if (col.type === 'number' || col.type === 'currency') {
columnStyles[index].halign = 'right';
} else if (col.type === 'text') {
if (col.type === "number" || col.type === "currency") {
columnStyles[index].halign = "right";
} else if (col.type === "text") {
// All text columns aligned left (including Cont)
columnStyles[index].halign = 'left';
columnStyles[index].halign = "left";
}
});
@@ -186,44 +193,49 @@ export const exportToPDF = (data, columns, filename, header) => {
styles: {
fontSize: 9,
cellPadding: 2.5,
valign: 'middle',
valign: "middle",
lineColor: [200, 200, 200],
lineWidth: 0.1,
overflow: 'linebreak'
overflow: "linebreak",
},
headStyles: {
fillColor: [41, 128, 185],
textColor: 255,
fontStyle: 'bold',
halign: 'center',
fontStyle: "bold",
halign: "center",
fontSize: 9,
cellPadding: 2.5
cellPadding: 2.5,
},
alternateRowStyles: {
fillColor: [248, 248, 248]
fillColor: [248, 248, 248],
},
columnStyles: columnStyles,
margin: { left: marginLeft, right: marginRight, top: tableStartY, bottom: 15 },
margin: {
left: marginLeft,
right: marginRight,
top: tableStartY,
bottom: 15,
},
tableWidth: pageWidth - marginLeft - marginRight, // Use full page width
theme: 'grid',
didDrawPage: function(data) {
theme: "grid",
didDrawPage: function (data) {
// Add header to each page
addHeader();
},
didParseCell: function(data) {
didParseCell: function (data) {
// Force alignment based on column type (body cells only)
if (data.section === 'body') {
if (data.section === "body") {
const colIndex = data.column.index;
const column = columns[colIndex];
if (column) {
if (column.type === 'number' || column.type === 'currency') {
data.cell.styles.halign = 'right';
} else if (column.type === 'text') {
if (column.type === "number" || column.type === "currency") {
data.cell.styles.halign = "right";
} else if (column.type === "text") {
if (colIndex === 0) {
data.cell.styles.halign = 'center';
data.cell.styles.halign = "center";
} else {
data.cell.styles.halign = 'left';
data.cell.styles.halign = "left";
}
}
}
@@ -247,18 +259,18 @@ export const exportToPDF = (data, columns, filename, header) => {
// Draw headers
doc.setFontSize(8);
doc.setFont(undefined, 'bold');
doc.setFont(undefined, "bold");
tableColumns.forEach((header, index) => {
doc.text(header, 14 + (index * 35), yPos);
doc.text(header, 14 + index * 35, yPos);
});
// Draw rows
doc.setFont(undefined, 'normal');
doc.setFont(undefined, "normal");
doc.setFontSize(7);
tableRows.forEach((row, rowIndex) => {
yPos += 7;
row.forEach((cell, cellIndex) => {
doc.text(String(cell), 14 + (cellIndex * 35), yPos);
doc.text(String(cell), 14 + cellIndex * 35, yPos);
});
});
@@ -267,11 +279,11 @@ export const exportToPDF = (data, columns, filename, header) => {
}
// Save PDF
doc.save(`${filename}_${new Date().toISOString().split('T')[0]}.pdf`);
doc.save(`${filename}_${new Date().toISOString().split("T")[0]}.pdf`);
return { success: true };
} catch (error) {
console.error('PDF export error details:', error);
return { success: false, error: error.message || 'PDF generation failed' };
console.error("PDF export error details:", error);
return { success: false, error: error.message || "PDF generation failed" };
}
};
@@ -281,31 +293,31 @@ export const exportToPDF = (data, columns, filename, header) => {
export const exportGeneralTotals = (summaryData) => {
const data = [
{
Tip: 'Clienți',
'Total Facturat': summaryData?.clienti_total_facturat || 0,
'Total Încasat': summaryData?.clienti_total_incasat || 0,
'Sold Net': summaryData?.clienti_sold_total || 0,
'Sold În Termen': summaryData?.clienti_sold_in_termen || 0,
'Sold Restant': summaryData?.clienti_sold_restant || 0
Tip: "Clienți",
"Total Facturat": summaryData?.clienti_total_facturat || 0,
"Total Încasat": summaryData?.clienti_total_incasat || 0,
"Sold Net": summaryData?.clienti_sold_total || 0,
"Sold În Termen": summaryData?.clienti_sold_in_termen || 0,
"Sold Restant": summaryData?.clienti_sold_restant || 0,
},
{
Tip: 'Furnizori',
'Total Facturat': summaryData?.furnizori_total_facturat || 0,
'Total Achitat': summaryData?.furnizori_total_achitat || 0,
'Sold Net': summaryData?.furnizori_sold_total || 0,
'Sold În Termen': summaryData?.furnizori_sold_in_termen || 0,
'Sold Restant': summaryData?.furnizori_sold_restant || 0
Tip: "Furnizori",
"Total Facturat": summaryData?.furnizori_total_facturat || 0,
"Total Achitat": summaryData?.furnizori_total_achitat || 0,
"Sold Net": summaryData?.furnizori_sold_total || 0,
"Sold În Termen": summaryData?.furnizori_sold_in_termen || 0,
"Sold Restant": summaryData?.furnizori_sold_restant || 0,
},
{
Tip: 'Trezorerie',
'Total Facturat': '-',
'Total Încasat/Achitat': '-',
'Sold Net': summaryData?.trezorerie_sold || 0,
'Sold În Termen': '-',
'Sold Restant': '-'
}
Tip: "Trezorerie",
"Total Facturat": "-",
"Total Încasat/Achitat": "-",
"Sold Net": summaryData?.trezorerie_sold || 0,
"Sold În Termen": "-",
"Sold Restant": "-",
},
];
return data;
};
@@ -315,27 +327,27 @@ export const exportGeneralTotals = (summaryData) => {
export const exportSoldNetBreakdown = (summaryData) => {
const data = [
{
Categorie: 'Clienți - Restant',
'TOTAL': summaryData?.clienti_sold_restant || 0,
'7 zile': summaryData?.clienti_restant_7 || 0,
'14 zile': summaryData?.clienti_restant_14 || 0,
'30 zile': summaryData?.clienti_restant_30 || 0,
'60 zile': summaryData?.clienti_restant_60 || 0,
'90 zile': summaryData?.clienti_restant_90 || 0,
'90+ zile': summaryData?.clienti_restant_over_90 || 0
Categorie: "Clienți - Restant",
TOTAL: summaryData?.clienti_sold_restant || 0,
"7 zile": summaryData?.clienti_restant_7 || 0,
"14 zile": summaryData?.clienti_restant_14 || 0,
"30 zile": summaryData?.clienti_restant_30 || 0,
"60 zile": summaryData?.clienti_restant_60 || 0,
"90 zile": summaryData?.clienti_restant_90 || 0,
"90+ zile": summaryData?.clienti_restant_over_90 || 0,
},
{
Categorie: 'Furnizori - Restant',
'TOTAL': summaryData?.furnizori_sold_restant || 0,
'7 zile': summaryData?.furnizori_restant_7 || 0,
'14 zile': summaryData?.furnizori_restant_14 || 0,
'30 zile': summaryData?.furnizori_restant_30 || 0,
'60 zile': summaryData?.furnizori_restant_60 || 0,
'90 zile': summaryData?.furnizori_restant_90 || 0,
'90+ zile': summaryData?.furnizori_restant_over_90 || 0
}
Categorie: "Furnizori - Restant",
TOTAL: summaryData?.furnizori_sold_restant || 0,
"7 zile": summaryData?.furnizori_restant_7 || 0,
"14 zile": summaryData?.furnizori_restant_14 || 0,
"30 zile": summaryData?.furnizori_restant_30 || 0,
"60 zile": summaryData?.furnizori_restant_60 || 0,
"90 zile": summaryData?.furnizori_restant_90 || 0,
"90+ zile": summaryData?.furnizori_restant_over_90 || 0,
},
];
return data;
};
@@ -349,14 +361,14 @@ export const exportTrendData = (trendsData, period, chartType) => {
const data = trendsData.labels.map((label, index) => {
const row = { Perioada: label };
trendsData.datasets.forEach(dataset => {
trendsData.datasets.forEach((dataset) => {
const value = dataset.data[index];
row[dataset.label] = value || 0;
});
return row;
});
return data;
};
};