first commit

master
Philipp 2025-08-25 16:35:35 +02:00
commit f1415db47c
14 changed files with 3294 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
node_modules
.vscode
config.js

31
README.md Normal file
View File

@ -0,0 +1,31 @@
# UpdateTrackingCodes
## Description
Dieses Programm liest eine Excel-Datei mit Sendungsnummern ein. Anhand der Lieferantenbestellnummer (LB-*****) wird der zugehörige Auftrag ermittelt.
Handelt es sich um einen Auftrag vom Otto Marketplace (kShop = 7), wird eine Rücksende-Sendungsnummer bei DHL generiert. Dies erfolgt durch den Aufruf (per exec) eines dafür bereitgestellten Programms.
Bei Aufträgen von anderen Marktplätzen wird derzeit keine Rücksende-Sendungsnummer benötigt.
Anschließend werden die relevanten Daten in die JTL-Datenbank geschrieben. Das Versanddatum wird dabei für alle Aufträge gesetzt – mit Ausnahme der Aufträge vom Otto Marketplace. In diesem Fall wird das Versanddatum erst eingetragen, nachdem die Versandinformationen über ein externes Programm an Otto übermittelt wurden.
### Executing program
```
node index.js
```
## Version History
* 1.0.0
* Initial Release
## License
## Acknowledgments

195
app.js Normal file
View File

@ -0,0 +1,195 @@
const FileManager = require("./file_manager")
const config = require("./config")
const execSync = require("child_process").execSync
const JtlSql = require("./jtlsql")
const DhlReturns = require("./dhl_returns")
const fileManager = new FileManager()
const jtlSql = new JtlSql()
const CsvLogger = require('./csv_logger')
const CsvExport = require('./csv_export')
const PATH_LOGFILES = require('./config').PATH_LOGFILES;
const FILE_PATH_CSV_EXPORT = require('./config').FILE_PATH_CSV_EXPORT;
const EXPECTED_PROPERTIES = require('./config').EXPECTED_PROPERTIES
const logger = new CsvLogger(PATH_LOGFILES, ['timestamp', 'level', 'message'], 30); // 30 Tage aufbewahren
const csvExport = new CsvExport(FILE_PATH_CSV_EXPORT)
const EmailSender = require("./email_sender")
const CsvToHtmlConverter = require("./csv_to_html_converter")
const path = require('path');
class App {
#warnOrErrOccured = false
#timestamp
constructor() {
this.#timestamp = Math.round(parseInt(Date.now()) / 1000)
logger.timestamp = this.#timestamp
process.on('uncaughtException', (err)=>{
this.#warnOrErrOccured = err
console.log(err)
this.#shutdown()
})
}
async run() {
logger.log('info',"Pogramm gestartet")
logger.log('info',"Lade Datei "+config.FILE_PATH_SENDUNGSNUMMERN)
let data = []
try {
data = fileManager.readExcelFile(config.FILE_PATH_SENDUNGSNUMMERN)
} catch (err) {
logger.log("err",err.message)
this.#warnOrErrOccured = err
await this.#shutdown()
}
await this.#checkDataStructure(Object.getOwnPropertyNames(data[0]))
logger.log('info', data.length+" Datensätze gefunden")
for (let i=0; i < data.length; i++) {
const liefBestNr = data[i]['Auftrags-Nr._1']
const order = await jtlSql.getOrderOnLiefBest(liefBestNr)
if (!order) {
logger.log('warn',"Keinen Auftrag gefunden zur Lieferantenbestellung: "+liefBestNr)
this.#warnOrErrOccured = true
continue
}
logger.log('info',"Auftrag '"+ order.cBestellNr+"' gefunden zur Lieferantenbestellung: "+liefBestNr)
await this.#handleShipping(data[i], order)
}
if (config.MOVE_FILE_TO_PROCESSED_FOLDER) {
await this.#moveFileToProcessedFolder(config.FILE_PATH_SENDUNGSNUMMERN)
}
await this.#shutdown()
}
async #shutdown() {
if (this.#warnOrErrOccured) {
//logger.log("err",this.#warnOrErrOccured.message)
if (config.SEND_MAIL_ON_EROR) {
let emailSender = new EmailSender()
let csvToHtmlConverter = new CsvToHtmlConverter()
await emailSender.sendMail({
to: defaultEmailConfig.to,
subject: "[Problem] Sendungsnummern Frank Flechtwaren",
html: `Last Error: <p style="font-weight: bold"">${this.#warnOrErrOccured.toString()}</p><br><br> ${await csvToHtmlConverter.convertToHtml(logger.getLogFilePath())}`
})
}
process.exit(1)
}
logger.log("info","Proramm beendet")
process.exit()
}
async #handleShipping(excelFileData, jtlOrderData) {
const sendDate = FileManager.convertDateExcel(excelFileData.Versanddatum)
console.log(excelFileData)
const lieferschein = await jtlSql.getLieferscheinOnAuftragsnummer(jtlOrderData.cBestellNr)
console.log(lieferschein)
const data = {
id: lieferschein.cLieferscheinNr,
date: sendDate.getDate()+'.'+ (sendDate.getMonth() +1) +'.'+sendDate.getFullYear(),
trackingNr: excelFileData['Sendungs-Nr.'],
infos: excelFileData['Lieferschein-Nr.'],
returnTrackingNr:''
}
if (jtlOrderData.kShop == 7) {
const paket = {}
paket.itemWeight = 1
paket.cReference = jtlOrderData.cBestellNr + " / " + jtlOrderData.cInetBestellNr
const labelData = await this.#createLabelData(excelFileData, jtlOrderData, paket)
data.returnTrackingNr = this.#getDhlReturnTrackingCode(labelData).toString()
console.log(labelData)
}
csvExport.add(data)
}
async #createLabelData(excelFileData, jtlOrderData, paketData) {
const liefAddress = await jtlSql.getAdresse(jtlOrderData.tAdresse_kAdresse)
const data = {
firma: liefAddress.cFirma,
name: excelFileData['Liefer-Name'] ? excelFileData['Liefer-Name'] : liefAddress.cVorname + ' '+ liefAddress.cNachname ,
strasse: excelFileData['Liefer-Str.'] ? excelFileData['Liefer-Str.'] : liefAddress.cStrasse,
plz: excelFileData['Liefer-Plz'] ? excelFileData['Liefer-Plz'] : liefAddress.cPLZ,
ort: excelFileData['Liefer-Stadt'] ? excelFileData['Liefer-Stadt'] : liefAddress.cOrt,
landISO2: liefAddress.cISO,
email: liefAddress.cMail,
itemWeight: paketData.fGewicht * 1000,
customerReference: paketData.cReference.replaceAll(" "," "),
shipmentReference: paketData.cReference.replaceAll(" "," "),
}
return data
}
#getDhlReturnTrackingCode(labelData) {
const dhlReturns = new DhlReturns()
const dhlShippingData = dhlReturns.createDhlShippingDataObject(labelData)
const returnTrackingCode = execSync(
"node ../dhlApi/index.js func=ReturnShipping data=\"" +
JSON.stringify(dhlShippingData).replaceAll('"', '\\"') +
"\""
)
return returnTrackingCode
}
async #checkDataStructure(proerties) {
if (proerties) {
try {
if (this.#hasDataStructureChanged(proerties)) {
throw new Error("DATA_STRUCTURE_OF_FILE_HAS_CHANGED")
}
logger.log("info", "Datenstruktur ist OK")
} catch (err) {
this.#warnOrErrOccured = err
logger.log('err', "Fehler in der Datenstruktur: " + err.message)
await this.#shutdown()
}
}
}
#hasDataStructureChanged(properties) {
return JSON.stringify(properties) !== JSON.stringify(EXPECTED_PROPERTIES)
}
async #moveFileToProcessedFolder(file) {
//Move file to processed
let fileManager = new FileManager()
let filename = this.#timestamp + "_" + path.basename(file)
try {
await fileManager.moveFile(file, config.PATH_TO_MOVE_PROCESSED_FILES + filename)
} catch (err) {
this.#warnOrErrOccured = err
logger.log('error', "Fehler beim verschieben: " + err.message)
}
}
}
module.exports = App

65
csv_export.js Normal file
View File

@ -0,0 +1,65 @@
const fs = require('fs');
const path = require('path');
class CsvExport {
/**
* @param {string} logDir - Directory where log files will be saved
* @param {string[]} headers - CSV header row
*/
constructor(filePath, headers = ['ID-Nummer', 'Datum (TT.MM.JJJ)', 'Tracking-Nr.','Versandinfos','Beilegeretoure']) {
this.filePath = filePath;
this.headers = headers;
if (!fs.existsSync(path.dirname(filePath))) {
fs.mkdirSync(path.dirname(filePath), { recursive: true });
} else {
fs.unlinkSync(filePath)
}
}
/**
* Writes a new log entry to today's CSV file
*/
add(data) {
this.ensureFileWithHeaders( this.filePath );
const row = [data.id, data.date, data.trackingNr, data.infos, data.returnTrackingNr]
.map(this.escapeCsv)
.join(';') + '\n';
fs.appendFileSync( this.filePath , row, (err) => {
if (err) {
console.error('Failed to write to csv file:', err);
}
});
}
/**
* Ensures headers are written if file doesn't exist
*/
ensureFileWithHeaders(filePath) {
if (!fs.existsSync(filePath)) {
const headerRow = this.headers.join(';') + '\n';
fs.writeFileSync(filePath, headerRow);
}
}
/**
* Escapes a value for safe CSV output
*/
escapeCsv(value) {
if (value == null) return '';
const str = String(value);
return /[",\n]/.test(str) ? `"${str.replace(/"/g, '""')}"` : str;
}
}
module.exports = CsvExport;

109
csv_logger.js Normal file
View File

@ -0,0 +1,109 @@
const fs = require('fs');
const path = require('path');
class CsvLogger {
/**
* @param {string} logDir - Directory where log files will be saved
* @param {string[]} headers - CSV header row
* @param {number} maxFileAgeDays - How many days log files should be kept
*/
constructor(logDir, headers = ['timestamp', 'level', 'message'], maxFileAgeDays = 30) {
this.logDir = logDir;
this.headers = headers;
this.maxFileAgeDays = maxFileAgeDays;
this.timestamp = null
if (!fs.existsSync(logDir)) {
fs.mkdirSync(logDir, { recursive: true });
}
// Cleanup old log files once on startup
this.deleteOldLogFiles().catch(console.error);
}
/**
* Writes a new log entry to today's CSV file
* @param {string} level - Log level (e.g., 'info', 'error')
* @param {string} message - Log message
* @param {string[]} extraFields - Optional additional fields
*/
log(level, message, extraFields = []) {
const timestamp = new Date().toISOString();
const filePath = this.getLogFilePath();
this.ensureFileWithHeaders(filePath);
const row = [timestamp, level, message, ...extraFields]
.map(this.escapeCsv)
.join(',') + '\n';
fs.appendFileSync(filePath, row, (err) => {
if (err) {
console.error('Failed to write to log file:', err);
}
});
}
/**
* Constructs the log file path for today
*/
getLogFilePath() {
const today = new Date().toISOString().split('T')[0]; // YYYY-MM-DD
let filename = "logs.csv"
if (this.timestamp == null) {
filename = `logs_${today}.csv`
} else {
filename = `${this.timestamp}_logs.csv`
}
return path.join(this.logDir, filename);
}
/**
* Ensures headers are written if file doesn't exist
*/
ensureFileWithHeaders(filePath) {
if (!fs.existsSync(filePath)) {
const headerRow = this.headers.join(',') + '\n';
fs.writeFileSync(filePath, headerRow);
}
}
/**
* Escapes a value for safe CSV output
*/
escapeCsv(value) {
if (value == null) return '';
const str = String(value);
return /[",\n]/.test(str) ? `"${str.replace(/"/g, '""')}"` : str;
}
/**
* Deletes log files older than maxFileAgeDays
*/
async deleteOldLogFiles() {
const files = await fs.promises.readdir(this.logDir);
const now = Date.now();
const maxAgeMs = this.maxFileAgeDays * 24 * 60 * 60 * 1000;
for (const file of files) {
const filePath = path.join(this.logDir, file);
if (!/^logs_\d{4}-\d{2}-\d{2}\.csv$/.test(file)) continue;
try {
const stats = await fs.promises.stat(filePath);
const age = now - stats.mtimeMs;
if (age > maxAgeMs) {
await fs.promises.unlink(filePath);
console.log(`Deleted old log file: ${file}`);
}
} catch (err) {
console.warn(`Could not process file ${file}: ${err.message}`);
}
}
}
}
module.exports = CsvLogger;

101
csv_to_html_converter.js Normal file
View File

@ -0,0 +1,101 @@
const fs = require('fs');
const readline = require('readline');
class CsvToHtmlConverter {
/**
* Erstellt eine HTML-Tabelle aus einer CSV-Datei
* @param {string} csvFilePath Pfad zur CSV-Datei
* @returns {Promise<string>} HTML-Tabelle als String
*/
async convertToHtml(csvFilePath) {
if (!fs.existsSync(csvFilePath)) {
throw new Error(`CSV file not found: ${csvFilePath}`);
}
const fileStream = fs.createReadStream(csvFilePath);
const rl = readline.createInterface({
input: fileStream,
crlfDelay: Infinity,
});
let html = this.getHtmlHeader();
let isFirstLine = true;
for await (const line of rl) {
const cells = this.parseCsvLine(line);
if (isFirstLine) {
html += '<thead><tr>';
cells.forEach(cell => {
html += `<th>${this.escapeHtml(cell)}</th>`;
});
html += '</tr></thead><tbody>';
isFirstLine = false;
} else {
html += '<tr>';
cells.forEach(cell => {
html += `<td>${this.escapeHtml(cell)}</td>`;
});
html += '</tr>';
}
}
html += '</tbody></table>';
return html;
}
/**
* Gibt das HTML- und CSS-Headergerüst für die Tabelle zurück
*/
getHtmlHeader() {
return `
<style>
table {
border-collapse: collapse;
width: 100%;
font-family: Arial, sans-serif;
}
th, td {
border: 1px solid #ccc;
padding: 6px 12px;
text-align: left;
}
thead {
background-color: #f2f2f2;
}
tbody tr:nth-child(odd) {
background-color: #ffffff;
}
tbody tr:nth-child(even) {
background-color: #f9f9f9;
}
</style>
<table>
`;
}
/**
* Wandelt eine CSV-Zeile in Felder um
* @param {string} line
* @returns {string[]}
*/
parseCsvLine(line) {
// Für einfache Fälle (Kommas, kein Quote-Parsing)
return line.split(',').map(field => field.trim());
}
/**
* Wandelt Sonderzeichen in HTML-Entities um
* @param {string} text
* @returns {string}
*/
escapeHtml(text) {
return String(text)
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;');
}
}
module.exports = CsvToHtmlConverter;

50
dhl_returns.js Normal file
View File

@ -0,0 +1,50 @@
class DhlReturns {
createDhlShippingDataObject(data) {
const orderData = {
receiverId: 'deu',
customerReference: data.customerReference,
shipmentReference: data.shipmentReference,
shipper: {
name1: data.name,
name2: data.firma,
name3: '',
addressStreet: this.#getStreet(data.strasse),
addressHouse: this.#getHouseNr(data.strasse),
city: data.ort,
email: '',
phone: '',
postalCode: data.plz,
state: ''
},
itemWeight: {
uom: 'g',
value: data.itemWeight
},
itemValue: {
currency: 'EUR',
value: 100
}
};
return orderData
}
#getStreet(str) {
let parts = str.split(" ")
let street = ''
for (let i = 0; i < parts.length-1; i++) {
street = street + parts[i] + " "
}
return street.trim()
}
#getHouseNr(str) {
let parts = str.split(" ")
return parts[parts.length -1].trim()
}
}
module.exports = DhlReturns

55
email_sender.js Normal file
View File

@ -0,0 +1,55 @@
const nodemailer = require('nodemailer');
const defaultEmailConfig = require('./config').defaultEmailConfig
class EmailSender {
/**
* Konstruktor initialisiert Transport-Konfiguration.
* @param {object} config - Konfiguration für den SMTP-Transport.
*/
constructor(config = undefined) {
if (!config) {
config = defaultEmailConfig
}
this.transporter = nodemailer.createTransport({
host: config.host,
port: config.port,
secure: config.secure, // true für 465, false für andere Ports
auth: {
user: config.user,
pass: config.pass
}
});
this.from = config.from;
}
/**
* Sendet eine E-Mail.
* @param {object} options - Details zur E-Mail.
* @param {string} options.to - Empfängeradresse.
* @param {string} options.subject - Betreff der E-Mail.
* @param {string} options.text - Nur-Text-Version.
* @param {string} [options.html] - HTML-Version (optional).
* @returns {Promise<void>}
*/
async sendMail({ to, subject, text, html }) {
try {
const info = await this.transporter.sendMail({
from: this.from,
to,
subject,
text,
html
});
console.log('E-Mail gesendet:', info.messageId);
} catch (error) {
console.error('Fehler beim Senden der E-Mail:', error);
throw error;
}
}
}
module.exports = EmailSender;

83
file_manager.js Normal file
View File

@ -0,0 +1,83 @@
const fsPromises = require('fs').promises;
const fs = require('fs');
const path = require('path');
const XLSX = require('xlsx');
class FileManager {
/**
* Moves a file from sourcePath to destinationPath.
* @param {string} sourcePath - Full path to the source file.
* @param {string} destinationPath - Destination directory or full file path.
*/
async moveFile(sourcePath, destinationPath) {
try {
const destinationStat = await fsPromises.lstat(destinationPath).catch(() => null);
const isDirectory = destinationStat && destinationStat.isDirectory();
const finalDestination = isDirectory
? path.join(destinationPath, path.basename(sourcePath))
: destinationPath;
await fsPromises.rename(sourcePath, finalDestination);
} catch (error) {
throw error;
}
}
readExcelFile(filePath) {
if (!fs.existsSync(filePath)) {
throw new Error(`Datei nicht gefunden: ${filePath}`);
}
const ext = path.extname(filePath).toLowerCase();
if (ext !== '.xlsx' && ext !== '.xls') {
throw new Error(`Ungültiger Dateityp: ${ext}`);
}
const workbook = XLSX.readFile(filePath);
const sheetName = workbook.SheetNames[0];
const worksheet = workbook.Sheets[sheetName];
const data = XLSX.utils.sheet_to_json(worksheet, { defval: null });
return data;
}
static async readJson(filePath) {
try {
const data = await fsPromises.readFile(filePath, 'utf-8');
return JSON.parse(data);
} catch (error) {
throw new Error(`Fehler beim Lesen der JSON-Datei: ${error.message}`);
}
}
static async writeJson(filePath, data) {
try {
const jsonString = JSON.stringify(data, null, 4); // schön formatiert
await fsPromises.writeFile(filePath, jsonString, 'utf-8');
} catch (error) {
throw new Error(`Fehler beim Schreiben der JSON-Datei: ${error.message}`);
}
}
static async copyFile(sourcePath, destinationPath) {
try {
await fsPromises.copyFile(sourcePath, destinationPath);
//console.log(`Datei erfolgreich kopiert von ${sourcePath} nach ${destinationPath}`);
} catch (error) {
throw new Error(`Fehler beim Kopieren der Datei: ${error.message}`);
}
}
static convertDateExcel (excelDate) {
// Get the number of milliseconds from Unix epoch.
const unixTime = (excelDate - 25569) * 86400 * 1000;
return new Date(unixTime);
}
}
module.exports = FileManager;

3
index.js Normal file
View File

@ -0,0 +1,3 @@
const App = require('./app')
new App().run()

189
jtlsql.js Normal file
View File

@ -0,0 +1,189 @@
const sql = require('mssql');
const MSSQL_CONFIG = require('./config').MSSQL_CONFIG
const DRY_RUN = require('./config').DRY_RUN
class JtlSql {
async getOrderOnLiefBest(liefBestNr) {
try {
const auftrag = await this.getBezugsAuftragsNrOnLiefBestNr(liefBestNr)
if (!auftrag) return null
await sql.connect(MSSQL_CONFIG);
const request = new sql.Request();
request.input('cBestellNr', sql.VarChar, auftrag.cBezugsAuftragsNummer);
const query = `SELECT * FROM tBestellung WHERE cBestellNr = @cBestellNr`;
const result = await request.query(query);
return result.recordset[0]
} catch (error) {
console.error('Error fetching BezugsAuftragsNummer:', error);
return null;
}
}
async getBezugsAuftragsNrOnLiefBestNr(liefBestNr) {
try {
await sql.connect(MSSQL_CONFIG);
const request = new sql.Request();
request.input('liefBestNr', sql.VarChar, liefBestNr);
const query = `SELECT cBezugsAuftragsNummer FROM tLieferantenBestellung WHERE cEigeneBestellnummer = @liefBestNr`;
const result = await request.query(query);
return result.recordset[0]
} catch (error) {
console.error('Error fetching BezugsAuftragsNummer:', error);
return null;
}
}
async getLieferscheinOnAuftragsnummer(auftragsNummer) {
try {
await sql.connect(MSSQL_CONFIG);
const request = new sql.Request();
request.input('auftragsNummer', sql.VarChar, auftragsNummer+'%');
const query = `
SELECT TOP 1 *
FROM tLieferschein
WHERE cLieferscheinNr LIKE @auftragsNummer
`;
const result = await request.query(query);
return result.recordset[0]|| null;
} catch (error) {
console.error('Error fetching Lieferschein:', error);
return null;
}
}
async getVersandPaketeOnLieferscheinId(kLieferschein) {
try {
await sql.connect(MSSQL_CONFIG);
const request = new sql.Request();
request.input('kLieferschein', sql.Int, kLieferschein);
const query = `
SELECT * FROM tVersand
WHERE kLieferschein = @kLieferschein
`;
const result = await request.query(query);
return result.recordset
} catch (error) {
console.error('Error fetching Versand:', error);
return null;
}
}
async saveVersandPaket(paket) {
if ('kVersand' in paket) {
return await this.updateVersandPaket(paket)
} else {
return await this.insertVersandPaket(paket)
}
}
async insertVersandPaket(paket) {
try {
await sql.connect(MSSQL_CONFIG);
const request = new sql.Request();
request.input('kLieferschein', sql.Int, paket.kLieferschein);
request.input('kBenutzer', sql.Int, paket.kBenutzer);
request.input('kLogistik', sql.Int, paket.kLogistik);
request.input('kVersandArt', sql.Int, paket.kVersandArt);
request.input('cIdentCode', sql.VarChar, paket.cIdentCode.toString());
request.input('cEnclosedReturnIdentCode', sql.VarChar, paket.cEnclosedReturnIdentCode.toString());
request.input('dVersendet', sql.DateTime, paket.dVersendet);
request.input('nStatus', sql.TinyInt, paket.nStatus);
const query = `
INSERT INTO tVersand
(kLieferschein, kBenutzer, kLogistik, nStatus, kVersandArt, cIdentCode, cEnclosedReturnIdentCode, dVersendet)
VALUES
(@kLieferschein, @kBenutzer, @kLogistik, @nStatus, @kVersandArt, @cIdentCode, @cEnclosedReturnIdentCode, @dVersendet)
`;
let fullQuery = DRY_RUN
? `BEGIN TRANSACTION;\n${query}\nROLLBACK TRANSACTION;`
: query;
const result = await request.query(fullQuery);
return result.rowsAffected[0]
} catch (error) {
throw error
}
}
async updateVersandPaket(paket) {
try {
await sql.connect(MSSQL_CONFIG);
const request = new sql.Request();
request.input('kVersand', sql.Int, paket.kVersand);
request.input('kLieferschein', sql.Int, paket.kLieferschein);
request.input('cIdentCode', sql.VarChar, paket.cIdentCode ? paket.cIdentCode.toString() : null);
request.input('cEnclosedReturnIdentCode', sql.VarChar, paket.cEnclosedReturnIdentCode ? paket.cEnclosedReturnIdentCode.toString() : null);
request.input('dVersendet', sql.DateTime, paket.dVersendet);
request.input('nStatus', sql.TinyInt, paket.nStatus);
const query = `
UPDATE tVersand SET
kLieferschein = @kLieferschein,
nStatus = @nStatus,
cIdentCode = @cIdentCode,
cEnclosedReturnIdentCode = @cEnclosedReturnIdentCode,
dVersendet = @dVersendet
WHERE
kVersand = @kVersand
`;
let fullQuery = DRY_RUN
? `BEGIN TRANSACTION;\n${query}\nROLLBACK TRANSACTION;`
: query;
const result = await request.query(fullQuery);
return result.rowsAffected[0]
} catch (error) {
throw error
}
}
async getAdresse(kAdresse) {
try {
await sql.connect(MSSQL_CONFIG);
const request = new sql.Request();
request.input('kAdresse', sql.Int, kAdresse);
const query = `SELECT * FROM tAdresse WHERE kAdresse = @kAdresse`;
const result = await request.query(query);
return result.recordset[0]
} catch (error) {
throw error
}
}
}
module.exports = JtlSql

2389
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

19
package.json Normal file
View File

@ -0,0 +1,19 @@
{
"name": "flechtware_updatetrackingcodes",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"description": "",
"dependencies": {
"mssql": "^11.0.1",
"nodemailer": "^7.0.3",
"path": "^0.12.7",
"readline": "^1.3.0",
"sqlite3": "^5.1.7",
"xlsx": "^0.18.5"
}
}

2
run.bat Normal file
View File

@ -0,0 +1,2 @@
cd "C:\Program Files\flechtware_generateTrackingCodesCSV"
node index.js