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