refactor: move scripts to ts_install directory
This commit is contained in:
64
ts_install/download-schematron.ts
Normal file
64
ts_install/download-schematron.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Script to download official Schematron files for e-invoice validation
|
||||
*/
|
||||
|
||||
import { SchematronDownloader } from '../ts/formats/validation/schematron.downloader.js';
|
||||
|
||||
async function main() {
|
||||
console.log('📥 Starting Schematron download...\n');
|
||||
|
||||
const downloader = new SchematronDownloader('assets/schematron');
|
||||
await downloader.initialize();
|
||||
|
||||
// Download EN16931 Schematron files
|
||||
console.log('🔵 Downloading EN16931 Schematron files...');
|
||||
try {
|
||||
const en16931Paths = await downloader.downloadStandard('EN16931');
|
||||
console.log(`✅ Downloaded ${en16931Paths.length} EN16931 files`);
|
||||
en16931Paths.forEach(p => console.log(` - ${p}`));
|
||||
} catch (error) {
|
||||
console.error(`❌ Failed to download EN16931: ${error.message}`);
|
||||
}
|
||||
|
||||
console.log('\n🔵 Downloading PEPPOL Schematron files...');
|
||||
try {
|
||||
const peppolPaths = await downloader.downloadStandard('PEPPOL');
|
||||
console.log(`✅ Downloaded ${peppolPaths.length} PEPPOL files`);
|
||||
peppolPaths.forEach(p => console.log(` - ${p}`));
|
||||
} catch (error) {
|
||||
console.error(`❌ Failed to download PEPPOL: ${error.message}`);
|
||||
}
|
||||
|
||||
console.log('\n🔵 Downloading XRechnung Schematron files...');
|
||||
try {
|
||||
const xrechnungPaths = await downloader.downloadStandard('XRECHNUNG');
|
||||
console.log(`✅ Downloaded ${xrechnungPaths.length} XRechnung files`);
|
||||
xrechnungPaths.forEach(p => console.log(` - ${p}`));
|
||||
} catch (error) {
|
||||
console.error(`❌ Failed to download XRechnung: ${error.message}`);
|
||||
}
|
||||
|
||||
// List cached files
|
||||
console.log('\n📂 Cached Schematron files:');
|
||||
const cached = await downloader.getCachedFiles();
|
||||
cached.forEach(file => {
|
||||
if (file.metadata) {
|
||||
console.log(` - ${file.path}`);
|
||||
console.log(` Version: ${file.metadata.version}`);
|
||||
console.log(` Format: ${file.metadata.format}`);
|
||||
console.log(` Downloaded: ${file.metadata.downloadDate}`);
|
||||
} else {
|
||||
console.log(` - ${file.path} (no metadata)`);
|
||||
}
|
||||
});
|
||||
|
||||
console.log('\n✅ Schematron download complete!');
|
||||
}
|
||||
|
||||
// Run the script
|
||||
main().catch(error => {
|
||||
console.error('❌ Script failed:', error);
|
||||
process.exit(1);
|
||||
});
|
205
ts_install/download-test-samples.ts
Normal file
205
ts_install/download-test-samples.ts
Normal file
@@ -0,0 +1,205 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Download official EN16931 and PEPPOL test samples for conformance testing
|
||||
*/
|
||||
|
||||
import * as https from 'https';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import { createWriteStream } from 'fs';
|
||||
import { pipeline } from 'stream/promises';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
interface TestSampleSource {
|
||||
name: string;
|
||||
description: string;
|
||||
repository: string;
|
||||
branch: string;
|
||||
paths: string[];
|
||||
targetDir: string;
|
||||
}
|
||||
|
||||
const TEST_SAMPLE_SOURCES: TestSampleSource[] = [
|
||||
{
|
||||
name: 'PEPPOL BIS 3.0 Examples',
|
||||
description: 'Official PEPPOL BIS Billing 3.0 example files',
|
||||
repository: 'OpenPEPPOL/peppol-bis-invoice-3',
|
||||
branch: 'master',
|
||||
paths: [
|
||||
'rules/examples/Allowance-example.xml',
|
||||
'rules/examples/base-example.xml',
|
||||
'rules/examples/base-negative-inv-correction.xml',
|
||||
'rules/examples/vat-category-E.xml',
|
||||
'rules/examples/vat-category-O.xml',
|
||||
'rules/examples/vat-category-S.xml',
|
||||
'rules/examples/vat-category-Z.xml',
|
||||
'rules/examples/vat-category-AE.xml',
|
||||
'rules/examples/vat-category-K.xml',
|
||||
'rules/examples/vat-category-G.xml'
|
||||
],
|
||||
targetDir: 'peppol-bis3'
|
||||
},
|
||||
{
|
||||
name: 'CEN TC434 Test Files',
|
||||
description: 'European Committee for Standardization test files',
|
||||
repository: 'ConnectingEurope/eInvoicing-EN16931',
|
||||
branch: 'master',
|
||||
paths: [
|
||||
'ubl/examples/ubl-tc434-example1.xml',
|
||||
'ubl/examples/ubl-tc434-example2.xml',
|
||||
'ubl/examples/ubl-tc434-example3.xml',
|
||||
'ubl/examples/ubl-tc434-example4.xml',
|
||||
'ubl/examples/ubl-tc434-example5.xml',
|
||||
'ubl/examples/ubl-tc434-example6.xml',
|
||||
'ubl/examples/ubl-tc434-example7.xml',
|
||||
'ubl/examples/ubl-tc434-example8.xml',
|
||||
'ubl/examples/ubl-tc434-example9.xml',
|
||||
'cii/examples/cii-tc434-example1.xml',
|
||||
'cii/examples/cii-tc434-example2.xml',
|
||||
'cii/examples/cii-tc434-example3.xml',
|
||||
'cii/examples/cii-tc434-example4.xml',
|
||||
'cii/examples/cii-tc434-example5.xml',
|
||||
'cii/examples/cii-tc434-example6.xml',
|
||||
'cii/examples/cii-tc434-example7.xml',
|
||||
'cii/examples/cii-tc434-example8.xml',
|
||||
'cii/examples/cii-tc434-example9.xml'
|
||||
],
|
||||
targetDir: 'cen-tc434'
|
||||
},
|
||||
{
|
||||
name: 'PEPPOL Validation Artifacts',
|
||||
description: 'PEPPOL validation test files',
|
||||
repository: 'OpenPEPPOL/peppol-bis-invoice-3',
|
||||
branch: 'master',
|
||||
paths: [
|
||||
'rules/unit-UBL/PEPPOL-EN16931-UBL.xml'
|
||||
],
|
||||
targetDir: 'peppol-validation'
|
||||
}
|
||||
];
|
||||
|
||||
/**
|
||||
* Download a file from GitHub
|
||||
*/
|
||||
async function downloadFile(
|
||||
repo: string,
|
||||
branch: string,
|
||||
filePath: string,
|
||||
targetPath: string
|
||||
): Promise<void> {
|
||||
const url = `https://raw.githubusercontent.com/${repo}/${branch}/${filePath}`;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
https.get(url, (response) => {
|
||||
if (response.statusCode === 404) {
|
||||
console.warn(` ⚠️ File not found: ${filePath}`);
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
if (response.statusCode !== 200) {
|
||||
reject(new Error(`Failed to download ${url}: ${response.statusCode}`));
|
||||
return;
|
||||
}
|
||||
|
||||
const dir = path.dirname(targetPath);
|
||||
if (!fs.existsSync(dir)) {
|
||||
fs.mkdirSync(dir, { recursive: true });
|
||||
}
|
||||
|
||||
const file = createWriteStream(targetPath);
|
||||
response.pipe(file);
|
||||
|
||||
file.on('finish', () => {
|
||||
file.close();
|
||||
console.log(` ✅ Downloaded: ${path.basename(filePath)}`);
|
||||
resolve();
|
||||
});
|
||||
|
||||
file.on('error', (err) => {
|
||||
fs.unlink(targetPath, () => {}); // Delete incomplete file
|
||||
reject(err);
|
||||
});
|
||||
}).on('error', reject);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Download test samples from a source
|
||||
*/
|
||||
async function downloadTestSamples(source: TestSampleSource): Promise<void> {
|
||||
console.log(`\n📦 ${source.name}`);
|
||||
console.log(` ${source.description}`);
|
||||
console.log(` Repository: ${source.repository}`);
|
||||
|
||||
const baseDir = path.join('test-samples', source.targetDir);
|
||||
|
||||
for (const filePath of source.paths) {
|
||||
const fileName = path.basename(filePath);
|
||||
const targetPath = path.join(baseDir, fileName);
|
||||
|
||||
try {
|
||||
await downloadFile(source.repository, source.branch, filePath, targetPath);
|
||||
} catch (error) {
|
||||
console.error(` ❌ Error downloading ${fileName}: ${error.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create metadata file for downloaded samples
|
||||
*/
|
||||
function createMetadata(sources: TestSampleSource[]): void {
|
||||
const metadata = {
|
||||
downloadDate: new Date().toISOString(),
|
||||
sources: sources.map(s => ({
|
||||
name: s.name,
|
||||
repository: s.repository,
|
||||
branch: s.branch,
|
||||
fileCount: s.paths.length
|
||||
})),
|
||||
totalFiles: sources.reduce((sum, s) => sum + s.paths.length, 0)
|
||||
};
|
||||
|
||||
const metadataPath = path.join('test-samples', 'metadata.json');
|
||||
fs.writeFileSync(metadataPath, JSON.stringify(metadata, null, 2));
|
||||
console.log('\n📝 Created metadata.json');
|
||||
}
|
||||
|
||||
/**
|
||||
* Main function
|
||||
*/
|
||||
async function main() {
|
||||
console.log('🚀 Downloading official EN16931 test samples...\n');
|
||||
|
||||
// Create base directory
|
||||
if (!fs.existsSync('test-samples')) {
|
||||
fs.mkdirSync('test-samples');
|
||||
}
|
||||
|
||||
// Download samples from each source
|
||||
for (const source of TEST_SAMPLE_SOURCES) {
|
||||
await downloadTestSamples(source);
|
||||
}
|
||||
|
||||
// Create metadata file
|
||||
createMetadata(TEST_SAMPLE_SOURCES);
|
||||
|
||||
console.log('\n✨ Test sample download complete!');
|
||||
console.log('📁 Samples saved to: test-samples/');
|
||||
|
||||
// Count total files
|
||||
const totalFiles = TEST_SAMPLE_SOURCES.reduce((sum, s) => sum + s.paths.length, 0);
|
||||
console.log(`📊 Total files: ${totalFiles}`);
|
||||
}
|
||||
|
||||
// Run if executed directly
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
|
||||
if (import.meta.url === `file://${process.argv[1]}`) {
|
||||
main().catch(console.error);
|
||||
}
|
||||
|
||||
export { downloadTestSamples, TEST_SAMPLE_SOURCES };
|
178
ts_install/download-xrechnung-rules.ts
Normal file
178
ts_install/download-xrechnung-rules.ts
Normal file
@@ -0,0 +1,178 @@
|
||||
#!/usr/bin/env tsx
|
||||
/**
|
||||
* Downloads official XRechnung Schematron validation rules
|
||||
* from the KoSIT repositories
|
||||
*/
|
||||
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import { execSync } from 'child_process';
|
||||
|
||||
const XRECHNUNG_VERSION = '3.0.2'; // Latest version as of 2025
|
||||
const VALIDATOR_VERSION = '2025-07-31'; // Next release date
|
||||
|
||||
const REPOS = {
|
||||
schematron: {
|
||||
url: 'https://github.com/itplr-kosit/xrechnung-schematron/archive/refs/tags/release-3.0.2.zip',
|
||||
dir: 'xrechnung-schematron'
|
||||
},
|
||||
validator: {
|
||||
url: 'https://github.com/itplr-kosit/validator-configuration-xrechnung/releases/download/release-2024-07-31/validator-configuration-xrechnung_3.0.1_2024-07-31.zip',
|
||||
dir: 'xrechnung-validator'
|
||||
}
|
||||
};
|
||||
|
||||
const ASSETS_DIR = path.join(process.cwd(), 'assets', 'schematron', 'xrechnung');
|
||||
|
||||
async function downloadFile(url: string, destination: string): Promise<void> {
|
||||
console.log(`Downloading ${url}...`);
|
||||
|
||||
try {
|
||||
// Use curl to download the file
|
||||
execSync(`curl -L -o "${destination}" "${url}"`, { stdio: 'inherit' });
|
||||
console.log(`Downloaded to ${destination}`);
|
||||
} catch (error) {
|
||||
console.error(`Failed to download ${url}:`, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async function extractZip(zipFile: string, destination: string): Promise<void> {
|
||||
console.log(`Extracting ${zipFile}...`);
|
||||
|
||||
try {
|
||||
// Create destination directory if it doesn't exist
|
||||
fs.mkdirSync(destination, { recursive: true });
|
||||
|
||||
// Extract using unzip
|
||||
execSync(`unzip -o "${zipFile}" -d "${destination}"`, { stdio: 'inherit' });
|
||||
console.log(`Extracted to ${destination}`);
|
||||
} catch (error) {
|
||||
console.error(`Failed to extract ${zipFile}:`, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async function downloadXRechnungRules(): Promise<void> {
|
||||
console.log('Starting XRechnung Schematron rules download...\n');
|
||||
|
||||
// Create assets directory
|
||||
fs.mkdirSync(ASSETS_DIR, { recursive: true });
|
||||
|
||||
const tempDir = path.join(ASSETS_DIR, 'temp');
|
||||
fs.mkdirSync(tempDir, { recursive: true });
|
||||
|
||||
// Download and extract Schematron rules
|
||||
console.log('1. Downloading XRechnung Schematron rules...');
|
||||
const schematronZip = path.join(tempDir, 'xrechnung-schematron.zip');
|
||||
await downloadFile(REPOS.schematron.url, schematronZip);
|
||||
|
||||
const schematronDir = path.join(ASSETS_DIR, REPOS.schematron.dir);
|
||||
await extractZip(schematronZip, schematronDir);
|
||||
|
||||
// Find the actual Schematron files
|
||||
const schematronExtractedDir = path.join(schematronDir, `xrechnung-schematron-release-${XRECHNUNG_VERSION}`);
|
||||
const schematronValidationDir = path.join(schematronExtractedDir, 'validation', 'schematron');
|
||||
|
||||
if (fs.existsSync(schematronValidationDir)) {
|
||||
console.log('\nFound Schematron validation files:');
|
||||
|
||||
// List UBL Schematron files
|
||||
const ublDir = path.join(schematronValidationDir, 'ubl-inv');
|
||||
if (fs.existsSync(ublDir)) {
|
||||
const ublFiles = fs.readdirSync(ublDir).filter(f => f.endsWith('.sch') || f.endsWith('.xsl'));
|
||||
console.log(' UBL Invoice Schematron:', ublFiles.join(', '));
|
||||
}
|
||||
|
||||
// List CII Schematron files
|
||||
const ciiDir = path.join(schematronValidationDir, 'cii');
|
||||
if (fs.existsSync(ciiDir)) {
|
||||
const ciiFiles = fs.readdirSync(ciiDir).filter(f => f.endsWith('.sch') || f.endsWith('.xsl'));
|
||||
console.log(' CII Schematron:', ciiFiles.join(', '));
|
||||
}
|
||||
|
||||
// Copy to final location
|
||||
const finalUblDir = path.join(ASSETS_DIR, 'ubl');
|
||||
const finalCiiDir = path.join(ASSETS_DIR, 'cii');
|
||||
|
||||
fs.mkdirSync(finalUblDir, { recursive: true });
|
||||
fs.mkdirSync(finalCiiDir, { recursive: true });
|
||||
|
||||
// Copy UBL files
|
||||
if (fs.existsSync(ublDir)) {
|
||||
const ublFiles = fs.readdirSync(ublDir);
|
||||
for (const file of ublFiles) {
|
||||
if (file.endsWith('.sch') || file.endsWith('.xsl')) {
|
||||
fs.copyFileSync(
|
||||
path.join(ublDir, file),
|
||||
path.join(finalUblDir, file)
|
||||
);
|
||||
}
|
||||
}
|
||||
console.log(`\nCopied UBL Schematron files to ${finalUblDir}`);
|
||||
}
|
||||
|
||||
// Copy CII files
|
||||
if (fs.existsSync(ciiDir)) {
|
||||
const ciiFiles = fs.readdirSync(ciiDir);
|
||||
for (const file of ciiFiles) {
|
||||
if (file.endsWith('.sch') || file.endsWith('.xsl')) {
|
||||
fs.copyFileSync(
|
||||
path.join(ciiDir, file),
|
||||
path.join(finalCiiDir, file)
|
||||
);
|
||||
}
|
||||
}
|
||||
console.log(`Copied CII Schematron files to ${finalCiiDir}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Download validator configuration (contains additional rules and scenarios)
|
||||
console.log('\n2. Downloading XRechnung validator configuration...');
|
||||
const validatorZip = path.join(tempDir, 'xrechnung-validator.zip');
|
||||
await downloadFile(REPOS.validator.url, validatorZip);
|
||||
|
||||
const validatorDir = path.join(ASSETS_DIR, REPOS.validator.dir);
|
||||
await extractZip(validatorZip, validatorDir);
|
||||
|
||||
// Create metadata file
|
||||
const metadata = {
|
||||
version: XRECHNUNG_VERSION,
|
||||
validatorVersion: VALIDATOR_VERSION,
|
||||
downloadDate: new Date().toISOString(),
|
||||
sources: {
|
||||
schematron: REPOS.schematron.url,
|
||||
validator: REPOS.validator.url
|
||||
},
|
||||
files: {
|
||||
ubl: fs.existsSync(path.join(ASSETS_DIR, 'ubl'))
|
||||
? fs.readdirSync(path.join(ASSETS_DIR, 'ubl')).filter(f => f.endsWith('.sch'))
|
||||
: [],
|
||||
cii: fs.existsSync(path.join(ASSETS_DIR, 'cii'))
|
||||
? fs.readdirSync(path.join(ASSETS_DIR, 'cii')).filter(f => f.endsWith('.sch'))
|
||||
: []
|
||||
}
|
||||
};
|
||||
|
||||
fs.writeFileSync(
|
||||
path.join(ASSETS_DIR, 'metadata.json'),
|
||||
JSON.stringify(metadata, null, 2)
|
||||
);
|
||||
|
||||
// Clean up temp directory
|
||||
console.log('\n3. Cleaning up...');
|
||||
fs.rmSync(tempDir, { recursive: true, force: true });
|
||||
|
||||
console.log('\n✅ XRechnung Schematron rules downloaded successfully!');
|
||||
console.log(`📁 Files are located in: ${ASSETS_DIR}`);
|
||||
console.log('\nNext steps:');
|
||||
console.log('1. Run Saxon-JS to compile .sch files to SEF format');
|
||||
console.log('2. Integrate with SchematronValidator');
|
||||
console.log('3. Add XRechnung-specific TypeScript validators');
|
||||
}
|
||||
|
||||
// Run the script
|
||||
downloadXRechnungRules().catch(error => {
|
||||
console.error('Failed to download XRechnung rules:', error);
|
||||
process.exit(1);
|
||||
});
|
Reference in New Issue
Block a user