Skip to content

Commit 8d262d8

Browse files
authored
Merge pull request #1 from redocmx/feat/addenda
feat: add addenda support
2 parents 0eb72e7 + a2aaf49 commit 8d262d8

File tree

7 files changed

+124
-72
lines changed

7 files changed

+124
-72
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "redocmx",
3-
"version": "0.0.5",
3+
"version": "0.0.6",
44
"description": "Conversión CFDI a PDF",
55
"main": "src/index.js",
66
"types": "src/types.d.ts",

src/addenda.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import File from './file.js';
2+
3+
export default class Addenda extends File {
4+
constructor() {
5+
super()
6+
}
7+
8+
replaceValues(content, options = null) {
9+
if(!options) return content
10+
11+
for(const option of Object.entries(options)) {
12+
const [key, value] = option
13+
content = content.split(key).join(value)
14+
}
15+
16+
return content
17+
}
18+
19+
async getFileContent(replaceValues) {
20+
const file = await this.getFile()
21+
const fileContent = file.content.toString()
22+
23+
return this.replaceValues(fileContent, replaceValues)
24+
}
25+
26+
}

src/cfdi.js

Lines changed: 25 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,90 +1,53 @@
1-
import fs from 'fs/promises';
21
import Service from './service.js';
32
import Pdf from './pdf.js';
3+
import Addenda from './addenda.js';
4+
import File from './file.js';
45

5-
const { F_OK, R_OK } = fs.constants;
6-
7-
export default class Cfdi {
6+
export default class Cfdi extends File {
87
constructor() {
8+
super();
99
this.pdf = null;
10-
this.addenda = null;
11-
this.filePath = null;
12-
this.fileBuffer = null;
13-
this.fileContent = null;
14-
this.service = Service.getInstance();
15-
}
1610

17-
fromFile(filePath) {
18-
this.filePath = filePath;
19-
return this;
20-
}
11+
this.addenda = null
12+
this.addendaReplaceValues = null
2113

22-
fromString(fileContent) {
23-
this.fileContent = fileContent;
24-
return this;
14+
this.service = Service.getInstance();
2515
}
2616

27-
async getXmlContent() {
28-
if(this.fileContent){
29-
return { content: this.fileContent, type: 'string' }
17+
setAddenda(addenda, replaceValues = null) {
18+
if (addenda && !(addenda instanceof Addenda)) {
19+
throw new TypeError('Addenda must be Addenda instance.');
3020
}
3121

32-
if(this.fileBuffer){
33-
return { content: this.fileBuffer, type: 'buffer' }
34-
}
35-
36-
if(this.filePath){
37-
try {
38-
await fs.access(this.filePath, F_OK | R_OK);
39-
} catch (err) {
40-
if (err.code === 'ENOENT') {
41-
throw new Error(`Failed to read XML content from file: ${this.filePath}. The file does not exist.`);
42-
} else if (err.code === 'EACCES') {
43-
throw new Error(`Permission denied: ${this.filePath}. The file exists but cannot be read.`);
44-
} else {
45-
throw err;
46-
}
47-
}
48-
49-
this.fileBuffer = await fs.readFile(this.filePath)
50-
return { content: this.fileBuffer, type: 'buffer' }
51-
}
52-
53-
throw new Error('XML content for the CFDI must be provided.');
54-
}
55-
56-
setAddenda ( addenda ) {
57-
if (typeof addenda !== 'string') {
58-
throw new TypeError('setAddenda function only accepts a string parameter.');
22+
if (replaceValues && Object.prototype.toString.call(replaceValues) !== '[object Object]') {
23+
throw new TypeError('Addenda replace values must be a valid key - value object.');
5924
}
6025

6126
this.addenda = addenda
27+
this.addendaReplaceValues = replaceValues
6228
}
6329

64-
getAddenda () {
65-
return this.addenda
66-
}
67-
68-
async toPdf( payload = {} ) {
69-
70-
if(this.pdf){
30+
async toPdf(payload = {}) {
31+
if (this.pdf) {
7132
return this.pdf;
7233
}
7334

7435
if (Object.prototype.toString.call(payload) !== '[object Object]') {
7536
throw new TypeError('toPdf function only accepts an object as a parameter.');
7637
}
7738

78-
const file = await this.getXmlContent();
79-
payload.format = 'pdf';
80-
81-
if (this.getAddenda()) {
82-
payload.addenda = this.getAddenda();
39+
const file = await this.getFile();
40+
41+
if (this.addenda) {
42+
const addendaContent = await this.addenda.getFileContent(this.addendaReplaceValues);
43+
payload.addenda = addendaContent;
8344
}
84-
85-
const result = await this.service.cfdisConvert({file, payload});
45+
46+
payload.format = 'pdf';
47+
48+
const result = await this.service.cfdisConvert({ file, payload });
8649
this.pdf = new Pdf(result);
87-
50+
8851
return this.pdf;
8952
}
9053
}

src/file.js

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import fs from 'fs/promises';
2+
3+
const { F_OK, R_OK } = fs.constants;
4+
5+
export default class File {
6+
constructor() {
7+
this.filePath = null;
8+
this.fileBuffer = null;
9+
this.fileContent = null;
10+
}
11+
12+
fromFile(filePath) {
13+
this.filePath = filePath;
14+
return this
15+
}
16+
17+
fromString(fileContent) {
18+
this.fileContent = fileContent;
19+
return this
20+
}
21+
22+
async getFile() {
23+
if (this.fileContent) {
24+
return { content: this.fileContent, type: 'string' }
25+
}
26+
27+
if (this.fileBuffer) {
28+
return { content: this.fileBuffer, type: 'buffer' }
29+
}
30+
31+
if (this.filePath) {
32+
try {
33+
await fs.access(this.filePath, F_OK | R_OK);
34+
} catch (err) {
35+
if (err.code === 'ENOENT') {
36+
throw new Error(`Failed to read content from file: ${this.filePath}. The file does not exist.`);
37+
} else if (err.code === 'EACCES') {
38+
throw new Error(`Permission denied: ${this.filePath}. The file exists but cannot be read.`);
39+
} else {
40+
throw err;
41+
}
42+
}
43+
44+
this.fileBuffer = await fs.readFile(this.filePath)
45+
return { content: this.fileBuffer, type: 'buffer' }
46+
}
47+
48+
throw new Error(`Failed to load file ${this.constructor.name}, you must use fromFile or fromString.`);
49+
}
50+
}

src/index.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import Cfdi from './cfdi.js';
2+
import Addenda from './addenda.js';
23
import Service from './service.js';
34

45
export default class Redoc {
@@ -8,7 +9,10 @@ export default class Redoc {
89
}
910

1011
get cfdi() {
11-
const cfdi = new Cfdi()
12-
return cfdi;
12+
return new Cfdi();
13+
}
14+
15+
get addenda() {
16+
return new Addenda()
1317
}
1418
}

src/service.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ export default class Service {
2121
async cfdisConvert({file, payload}) {
2222

2323
const formData = new FormData();
24-
24+
2525
if(payload?.style_pdf){
2626
formData.append('style_pdf', payload.style_pdf);
2727
}

src/types.d.ts

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,36 @@
11
import Cfdi from './cfdi';
22
import Service from './service';
33
import Pdf from './pdf';
4+
import Addenda from './addenda';
45

56
export default class Redoc {
67
apiKey: string;
78
service: Service;
89
constructor(apiKey?: string);
910
get cfdi(): Cfdi;
11+
get addenda(): Addenda;
1012
}
1113

12-
export default class Cfdi {
14+
export default class Cfdi extends File {
1315
pdf: Pdf | null;
14-
addenda: string | null;
16+
addenda: Addenda | null;
17+
constructor();
18+
setAddenda(addenda: Addenda, replaceValues?: object): void;
19+
toPdf(payload?: CfdisConvertPayload): Promise<Pdf>;
20+
}
21+
export default class Addenda extends File {
22+
constructor();
23+
getFileContent(replaceValues?: object): Promise<string>;
24+
}
25+
26+
export default class File {
1527
filePath: string | null;
1628
fileBuffer: Buffer | null;
1729
fileContent: string | null;
1830
constructor();
1931
fromFile(filePath: string): this;
2032
fromString(fileContent: string): this;
21-
getXmlContent(): Promise<{ content: string | Buffer; type: 'string' | 'buffer' }>;
22-
setAddenda(addenda: string): void;
23-
getAddenda(): string | null;
24-
toPdf(payload?: CfdisConvertPayload): Promise<Pdf>;
33+
getFile(): Promise<{ content: string | Buffer; type: 'string' | 'buffer' }>;
2534
}
2635

2736
export interface CfdisMetadata {

0 commit comments

Comments
 (0)