@e-invoice-eu/cli
Version:
Generate e-invoices (E-Rechnung in German) conforming to EN16931 (Factur-X/ZUGFeRD, UBL, CII, XRechnung aka X-Rechnung) from LibreOffice Calc/Excel data or JSON.
333 lines • 12 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.Invoice = void 0;
exports.guessLibreOfficePath = guessLibreOfficePath;
const core_1 = require("@e-invoice-eu/core");
const runtime_1 = require("@esgettext/runtime");
const fs_1 = require("fs");
const fs = __importStar(require("fs/promises"));
const yaml = __importStar(require("js-yaml"));
const mime_types_1 = require("mime-types");
const os = __importStar(require("os"));
const path = __importStar(require("path"));
const optspec_1 = require("../optspec");
const package_1 = require("../package");
const safe_stdout_write_1 = require("../safe-stdout-write");
const gtx = runtime_1.Textdomain.getInstance('e-invoice-eu-cli');
function findExecutable(command) {
const paths = process.env.PATH?.split(path.delimiter) || [];
for (const dir of paths) {
const fullPath = path.join(dir, command);
try {
if ((0, fs_1.statSync)(fullPath).isFile() &&
(0, fs_1.accessSync)(fullPath, fs.constants.X_OK) === undefined) {
return fullPath;
}
}
catch {
}
}
return null;
}
function guessLibreOfficePath() {
const platform = os.platform();
if (platform === 'win32') {
return 'C:\\Program Files\\LibreOffice\\program\\soffice.exe';
}
else if (platform === 'darwin') {
return '/Applications/LibreOffice.app/Contents/MacOS/soffice';
}
else {
return (findExecutable('libreoffice') ??
findExecutable('soffice') ??
'libreoffice');
}
}
const options = {
format: {
group: gtx._('Format selection'),
alias: ['f'],
type: 'string',
demandOption: true,
describe: gtx._("invoice format (case-insensitive), try 'format --list' for a list of allowed values"),
},
output: {
group: gtx._('Output file location'),
alias: ['o'],
type: 'string',
demandOption: false,
describe: gtx._('write output to specified file instead of standard output'),
},
invoice: {
group: gtx._('Input data'),
alias: ['i'],
type: 'string',
conflicts: ['mapping'],
demandOption: false,
describe: gtx._('JSON file with invoice data, mandatory for json data input'),
},
spreadsheet: {
group: gtx._('Input data'),
alias: ['s'],
type: 'string',
demandOption: false,
describe: gtx._('invoice spreadsheet data, mandatory for spreadsheet data input'),
},
mapping: {
group: gtx._('Input data'),
alias: ['m'],
type: 'string',
conflicts: ['invoice'],
demandOption: false,
describe: gtx._('mapping file (YAML or JSON), mandatory for spreadsheet data input'),
},
pdf: {
group: gtx._('Input data'),
alias: ['p'],
type: 'string',
demandOption: false,
describe: gtx._('PDF version of the invoice'),
},
'pdf-id': {
group: gtx._('Input data'),
type: 'string',
demandOption: false,
describe: gtx._('ID of the embedded PDF, defaults to the document number'),
},
'pdf-description': {
group: gtx._('Input data'),
type: 'string',
demandOption: false,
describe: gtx._('optional description of the embedded PDF'),
},
attachment: {
group: gtx._('Input data'),
alias: ['a'],
type: 'string',
multi: true,
demandOption: false,
describe: gtx._('arbitrary number of attachments'),
},
'attachment-id': {
group: gtx._('Input data'),
type: 'string',
multi: true,
demandOption: false,
describe: gtx._('optional ids of the attachments'),
},
'attachment-description': {
group: gtx._('Input data'),
type: 'string',
multi: true,
demandOption: false,
describe: gtx._('optional descriptions of the attachments'),
},
'attachment-mimetype': {
group: gtx._('Input data'),
alias: ['attachment-mime-type'],
type: 'string',
multi: true,
demandOption: false,
describe: gtx._('optional MIME types of the attachments'),
},
lang: {
group: gtx._('Invoice details'),
alias: ['l'],
type: 'string',
demandOption: false,
default: 'en',
describe: gtx._('invoice language code'),
},
'embed-pdf': {
group: gtx._('Invoice details'),
type: 'boolean',
demandOption: false,
describe: gtx._('embed a PDF version of the invoice'),
},
'libre-office': {
group: gtx._('External programs'),
alias: ['libreoffice'],
type: 'string',
demandOption: false,
default: guessLibreOfficePath(),
describe: gtx._('path to LibreOffice executable, mandatory if PDF creation is requested'),
},
};
class Invoice {
description() {
return gtx._('Create an e-invoice from spreadsheet data or JSON.');
}
aliases() {
return [];
}
build(argv) {
return argv.options(options);
}
async addPdf(options, configOptions) {
if (configOptions.pdf) {
options.pdf = {
buffer: await fs.readFile(configOptions.pdf),
filename: path.basename(configOptions.pdf),
mimetype: 'application/pdf',
id: configOptions['pdf-id'],
description: configOptions['pdf-id'],
};
options.embedPDF = !!configOptions['embed-pdf'];
}
}
checkConfigOptions(configOptions) {
if (typeof configOptions.invoice === 'undefined' &&
typeof configOptions.mapping === 'undefined') {
throw new Error(gtx._("One of the options '--invoice' or '--mapping' is mandatory."));
}
else if (typeof configOptions.mapping !== 'undefined' &&
typeof configOptions.spreadsheet === 'undefined') {
throw new Error(gtx._('No invoice spreadsheet specified.'));
}
}
async addAttachments(options, configOptions) {
if (!configOptions.attachment)
return;
const attachments = configOptions.attachment;
for (let i = 0; i < attachments.length; ++i) {
const filename = attachments[i];
const basename = path.basename(filename);
const mimetype = configOptions['attachment-mimetype']?.[i] ?? (0, mime_types_1.lookup)(basename);
if (!mimetype) {
throw new Error(gtx._x("cannot guess MIME type of attachment '{filename}'!", {
filename,
}));
}
options.attachments ??= [];
options.attachments.push({
buffer: await fs.readFile(filename),
filename: basename,
mimetype,
id: configOptions['attachment-id']?.[i],
description: configOptions['attachment-description']?.[i],
});
}
}
async createInvoice(options, configOptions) {
let invoiceData;
const format = configOptions.format;
if (typeof configOptions.spreadsheet !== 'undefined') {
options.spreadsheet = {
filename: configOptions.spreadsheet,
buffer: await fs.readFile(configOptions.spreadsheet),
mimetype: mime_types_1.lookup[configOptions.spreadsheet],
};
}
if (typeof configOptions.invoice !== 'undefined') {
const filename = configOptions.invoice;
const json = await fs.readFile(filename, 'utf-8');
try {
invoiceData = JSON.parse(json);
}
catch (e) {
throw new Error(`${filename}: ${e.message}`);
}
}
else if (typeof configOptions.mapping !== 'undefined') {
if (typeof configOptions.spreadsheet == 'undefined') {
throw new Error(gtx._("The option '--spreadsheet' is mandatory if a mapping is specified!"));
}
const mappingYaml = await fs.readFile(configOptions.mapping, 'utf-8');
const mapping = yaml.load(mappingYaml);
const mappingService = new core_1.MappingService(console);
invoiceData = mappingService.transform(options.spreadsheet.buffer, format.toLowerCase(), mapping);
}
else {
throw new Error(gtx._("You must either specify '--data' or '--invoice'!"));
}
options.libreOfficePath = configOptions['libre-office'];
const invoiceService = new core_1.InvoiceService(console);
return await invoiceService.generate(invoiceData, options);
}
async doRun(configOptions) {
const options = {
format: configOptions.format,
lang: configOptions.lang,
attachments: [],
};
this.checkConfigOptions(configOptions);
await this.addPdf(options, configOptions);
await this.addAttachments(options, configOptions);
const document = await this.createInvoice(options, configOptions);
if (typeof document === 'string') {
if (typeof configOptions.output === 'undefined') {
(0, safe_stdout_write_1.safeStdoutWrite)(document);
}
else {
await fs.writeFile(configOptions.output, document, 'utf-8');
}
}
else {
if (typeof configOptions.output === 'undefined') {
(0, safe_stdout_write_1.safeStdoutBufferWrite)(document);
}
else {
await fs.writeFile(configOptions.output, document);
}
}
}
async run(argv) {
const configOptions = argv;
if (!(0, optspec_1.coerceOptions)(argv, options)) {
return 1;
}
try {
await this.doRun(configOptions);
return 0;
}
catch (e) {
if (e.ajv) {
console.error(gtx._x('{programName}: fatal error:', {
programName: package_1.Package.getName(),
}), e.errors);
}
else {
console.error(gtx._x('{programName}: {error}', {
programName: package_1.Package.getName(),
error: e,
}));
}
return 1;
}
}
}
exports.Invoice = Invoice;
//# sourceMappingURL=invoice.js.map