Skip to content

Commit edc1289

Browse files
authored
Merge pull request #80 from MisaelMa/fix/publish-commit-and-summary
fix(publish): remove --apply, add summary, fix version commit
2 parents 0d0d3a6 + b6ba1ec commit edc1289

56 files changed

Lines changed: 2508 additions & 220 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 343 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,343 @@
1+
---
2+
name: cfdi-complemento
3+
description: >
4+
Guide for adding new SAT fiscal complement (complemento) support to the cfdi-node monorepo.
5+
Use this skill whenever someone wants to: add a new complemento, implement a missing complement,
6+
create support for a SAT fiscal addon (like Hidrocarburos, Nomina, CartaPorte, etc.),
7+
or asks about how complements work in this project. Also trigger when the user mentions
8+
"complemento", "complement", "addon fiscal", "XSD del SAT", or references any SAT namespace
9+
like "http://www.sat.gob.mx/...". Even if the user just says "add hidrocarburos support"
10+
or "implement pagos" without explicitly mentioning "complement", this skill applies.
11+
---
12+
13+
# Adding a New Complemento to cfdi-node
14+
15+
This guide walks you through implementing a new SAT fiscal complement in the `@cfdi/complementos` package. The project lives at `packages/cfdi/complementos/` inside the Rush monorepo.
16+
17+
## Understanding the Architecture
18+
19+
Every CFDI complement follows the same core idea: it's a TypeScript class that builds an XML fragment with the right SAT namespace, schema location, and attributes. The class extends the abstract `Complemento<T>` base class, which handles namespace registration and exposes `getComplement()` — the standardized method the CFDI builder uses to attach the complement to the invoice XML.
20+
21+
The base class (`src/Complemento.ts`) does the heavy lifting:
22+
23+
```typescript
24+
abstract class Complemento<T = any> {
25+
public complemento: T = {} as T;
26+
// Manages: xmlns, key, xmlnskey, schemaLocation
27+
constructor(config: { xmlns: string; key: string; xsd: string }) { ... }
28+
public getComplement(): ComplementsReturn<T> { ... }
29+
}
30+
```
31+
32+
You define your XML structure as a generic type `T`, pass the SAT namespace info to `super()`, and build the complement data in your constructor and methods.
33+
34+
## Step-by-Step: Adding a New Complement
35+
36+
### 1. Gather SAT Requirements
37+
38+
Before writing code, you need these from the SAT specification or XSD:
39+
40+
- **Namespace URI** (xmlns): e.g. `http://www.sat.gob.mx/hidrocarburospetroliferos`
41+
- **XSD URL**: e.g. `http://www.sat.gob.mx/sitio_internet/cfd/hidrocarburos/hidrocarburos.xsd`
42+
- **XML element name with prefix**: e.g. `hidrocarburos:Hidrocarburos`
43+
- **Attributes and nested elements**: read the XSD to understand the full structure
44+
- **Catalogs/enums**: fixed value sets like permit types, product codes, etc.
45+
- **Whether it's a root complement or concept-level complement**:
46+
- Root complements go in `Comprobante/Complemento/` (most common)
47+
- Concept complements go in `Comprobante/Conceptos/Concepto/ComplementoConcepto/`
48+
49+
### 2. Create the Type Definitions
50+
51+
Create types inside `src/4.0/<nombre>/type/`.
52+
53+
#### Attributes Interface
54+
55+
Every XML element has an `_attributes` property. Define an interface for it:
56+
57+
```typescript
58+
// Example: src/4.0/hidrocarburos/type/hidrocarburos.xslt.ts
59+
60+
export interface XmlHidrocarburos {
61+
_attributes: XmlHidrocarburosAttributes;
62+
}
63+
64+
export interface XmlHidrocarburosAttributes {
65+
Version: string;
66+
TipoPermiso: string; // Use enum type if you define one
67+
NumeroPermiso: string;
68+
ClaveHYP: string;
69+
SubProductoHYP?: string; // Optional attributes get '?'
70+
}
71+
```
72+
73+
The naming convention is: `Xml` + PascalCase name for the main interface, and `Xml` + PascalCase name + `Attributes` for the attributes.
74+
75+
#### Nested Elements
76+
77+
If the complement has child elements, add them to the main interface using the namespaced key:
78+
79+
```typescript
80+
export interface XmlAerolineas {
81+
_attributes: XmlAerolineasAttributes;
82+
'aerolineas:OtrosCargos': XmlAerolineasOtrosCargos; // nested element
83+
}
84+
```
85+
86+
Arrays of elements use `Type[]`:
87+
88+
```typescript
89+
export interface XmlAerolineasOtrosCargos {
90+
_attributes?: XmlAerolineasOtrosCargosAttributes;
91+
'aerolineas:Cargo': XmlAerolineasCargo[]; // array of children
92+
}
93+
```
94+
95+
#### Enums (if needed)
96+
97+
If the SAT defines a fixed catalog of values, create an enum file:
98+
99+
```typescript
100+
// Example: hidrocarburos.enum.ts
101+
102+
export enum TipoPermiso {
103+
PER06 = 'PER06',
104+
PER07 = 'PER07',
105+
PER08 = 'PER08',
106+
}
107+
108+
export const TipoPermisoList = [
109+
{ value: 'PER06', label: 'Distribuidor de petroliferos' },
110+
{ value: 'PER07', label: 'Expendio al publico de petroliferos' },
111+
{ value: 'PER08', label: 'Distribuidor y expendio al publico' },
112+
];
113+
```
114+
115+
#### Barrel Export
116+
117+
Create an `index.ts` in the type directory:
118+
119+
```typescript
120+
export * from './hidrocarburos.xslt';
121+
// export * from './hidrocarburos.enum'; // if enums exist
122+
```
123+
124+
### 3. Create the Complement Class
125+
126+
Create the class at `src/4.0/<nombre>/<Nombre>.ts`.
127+
128+
#### Simple Complement (flat attributes only)
129+
130+
This is the most common case:
131+
132+
```typescript
133+
// src/4.0/hidrocarburos/Hidrocarburos.ts
134+
135+
import { XmlHidrocarburos, XmlHidrocarburosAttributes } from './type/hidrocarburos.xslt';
136+
import { Complemento } from '../../Complemento';
137+
138+
const xmlns = 'http://www.sat.gob.mx/hidrocarburospetroliferos';
139+
const xsd = 'http://www.sat.gob.mx/sitio_internet/cfd/hidrocarburos/hidrocarburos.xsd';
140+
141+
export class Hidrocarburos extends Complemento<XmlHidrocarburos> {
142+
public complemento: XmlHidrocarburos = {} as XmlHidrocarburos;
143+
144+
constructor(attributes: XmlHidrocarburosAttributes) {
145+
super({ key: 'hidrocarburos:Hidrocarburos', xmlns, xsd });
146+
this.complemento._attributes = attributes;
147+
}
148+
}
149+
```
150+
151+
The three values passed to `super()` are critical:
152+
- `key`: the namespaced XML element name (prefix:ElementName)
153+
- `xmlns`: the SAT namespace URI
154+
- `xsd`: the full URL to the XSD schema file
155+
156+
#### Complex Complement (with nested elements)
157+
158+
When a complement has child elements, add builder methods. Follow the Aerolineas pattern:
159+
160+
```typescript
161+
export class MiComplemento extends Complemento<XmlMiComplemento> {
162+
public complemento: XmlMiComplemento = {} as XmlMiComplemento;
163+
164+
constructor(attributes: XmlMiComplementoAttributes) {
165+
super({ key: 'prefix:ElementName', xmlns, xsd });
166+
this.complemento._attributes = attributes;
167+
}
168+
169+
addChild(attributes: XmlChildAttributes): void {
170+
if (!this.complemento['prefix:Children']) {
171+
this.complemento['prefix:Children'] = [];
172+
}
173+
this.complemento['prefix:Children'].push({
174+
_attributes: attributes,
175+
});
176+
}
177+
}
178+
```
179+
180+
Important behaviors:
181+
- Initialize arrays/objects lazily (check if they exist first)
182+
- Throw descriptive errors if ordering matters (e.g., parent must exist before adding children)
183+
- Use `void` return type for builder methods (unless you want fluent chaining with `this`)
184+
185+
### 4. Create the Barrel Export
186+
187+
Add an `index.ts` in the complement directory:
188+
189+
```typescript
190+
// src/4.0/hidrocarburos/index.ts
191+
export * from './Hidrocarburos';
192+
export * from './type';
193+
```
194+
195+
### 5. Register the Complement
196+
197+
This is where you wire the new complement into the rest of the system. There are several files to update:
198+
199+
#### a) Main package export — `src/index.ts`
200+
201+
Add the export line with the other 4.0 exports:
202+
203+
```typescript
204+
export * from './4.0/hidrocarburos';
205+
```
206+
207+
#### b) Complement interfaces — `src/types/tags/complements.interface.ts`
208+
209+
**For root complements**, add to `XmlComplements`:
210+
211+
```typescript
212+
export interface XmlComplements extends AnyKey {
213+
// ... existing entries ...
214+
'hidrocarburos:Hidrocarburos'?: XmlHidrocarburos;
215+
}
216+
```
217+
218+
**For concept-level complements**, add to `XmlComplementsConcepts`:
219+
220+
```typescript
221+
export interface XmlComplementsConcepts extends AnyKey {
222+
// ... existing entries ...
223+
'myprefix:MyElement'?: XmlMyType;
224+
}
225+
```
226+
227+
#### c) Type unions — same file
228+
229+
Add the class to `ComlementType` (note: the typo "Comlement" is intentional, it exists this way in the codebase — don't "fix" it):
230+
231+
```typescript
232+
export declare type ComlementType =
233+
| // ... existing ...
234+
| Hidrocarburos
235+
| Complemento;
236+
```
237+
238+
Add the XML type to `ComplementTypeXml<T>`:
239+
240+
```typescript
241+
export declare type ComplementTypeXml<T> =
242+
| // ... existing ...
243+
| XmlHidrocarburos
244+
| T;
245+
```
246+
247+
#### d) Namespace attributes — same file
248+
249+
Add namespace declaration to `XmlComplementsAttributes`:
250+
251+
```typescript
252+
'xmlns:hidrocarburos'?: string; // http://www.sat.gob.mx/hidrocarburospetroliferos
253+
```
254+
255+
Add to `XmlnsComplementsLinks`:
256+
257+
```typescript
258+
hidrocarburos?: string; // http://www.sat.gob.mx/hidrocarburospetroliferos
259+
```
260+
261+
### 6. Write Tests
262+
263+
Create a test file using Vitest:
264+
265+
```typescript
266+
// test/hidrocarburos.test.ts
267+
import { describe, it, expect } from 'vitest';
268+
import { Hidrocarburos } from '../src/4.0/hidrocarburos/Hidrocarburos';
269+
270+
describe('Hidrocarburos', () => {
271+
it('should create a valid complement with required attributes', () => {
272+
const hidro = new Hidrocarburos({
273+
Version: '1.0',
274+
TipoPermiso: 'PER06',
275+
NumeroPermiso: 'PL/12345/EXP/2026',
276+
ClaveHYP: '01',
277+
SubProductoHYP: '0101',
278+
});
279+
280+
const result = hidro.getComplement();
281+
expect(result.key).toBe('hidrocarburos:Hidrocarburos');
282+
expect(result.xmlns).toBe('http://www.sat.gob.mx/hidrocarburospetroliferos');
283+
expect(result.complement._attributes.Version).toBe('1.0');
284+
expect(result.schemaLocation).toHaveLength(2);
285+
});
286+
});
287+
```
288+
289+
Tests should validate: correct `key`, `xmlns`, `schemaLocation`, attributes, nested element construction, and error cases.
290+
291+
Run with: `npm run test:ci` from the package directory, or `rush test:ci` from the monorepo root.
292+
293+
### 7. Build and Verify
294+
295+
```bash
296+
npm run build
297+
npm run test:ci
298+
```
299+
300+
## Quick Reference: File Checklist
301+
302+
For a new complement called `<Nombre>`:
303+
304+
```
305+
CREATE:
306+
src/4.0/<nombre>/
307+
<Nombre>.ts # Main class extending Complemento<T>
308+
index.ts # Barrel export
309+
type/
310+
<nombre>.xslt.ts # XML interfaces (Xml*, Xml*Attributes)
311+
<nombre>.enum.ts # Enums (if SAT defines catalogs)
312+
index.ts # Type barrel export
313+
314+
UPDATE:
315+
src/index.ts # Add export
316+
src/types/tags/complements.interface.ts # Add to:
317+
- XmlComplements or XmlComplementsConcepts # interface mapping
318+
- ComlementType # class union
319+
- ComplementTypeXml<T> # XML type union
320+
- XmlComplementsAttributes # xmlns declaration
321+
- XmlnsComplementsLinks # namespace links
322+
323+
CREATE (optional):
324+
test/<nombre>.test.ts # Vitest tests
325+
```
326+
327+
## Common Pitfalls
328+
329+
- **Forgetting to register in `complements.interface.ts`**: The complement will work in isolation but won't be recognized by the CFDI builder. This is the most commonly missed step.
330+
- **Wrong namespace prefix in key**: The part before the colon in `key` must match the namespace prefix used in the XML interfaces (e.g., `'aerolineas:Aerolineas'` uses prefix `aerolineas`).
331+
- **XSD URL mismatch**: Double-check the XSD URL against the SAT's official site. A wrong URL means validation will fail.
332+
- **Missing `_attributes` property**: Every XML element interface needs `_attributes` — this is how `xml-js` represents XML attributes.
333+
- **Not re-exporting from index.ts**: Both the complement directory and the main `src/index.ts` need export lines.
334+
- **"Fixing" the ComlementType typo**: It's `ComlementType` not `ComplementType` throughout the codebase. Don't rename it.
335+
336+
## Reference: Existing Complement Examples
337+
338+
Read these files to see the patterns in action:
339+
340+
- **Simple**: `src/4.0/iedu/Iedu.ts` — flat attributes, extends Complemento
341+
- **With children**: `src/4.0/aerolineas/Aerolineas.ts` — nested elements, builder methods
342+
- **Complex**: `src/4.0/cartaporte20/CartaPorte20.ts` — multiple nested helpers, setter methods
343+
- **Concept-level**: `src/4.0/iedu/Iedu.ts` — attaches to individual concepts, not root

0 commit comments

Comments
 (0)