-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #211 from invopop/food-vouchers
Food vouchers complement in MX
- Loading branch information
Showing
5 changed files
with
403 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
$schema: "https://gobl.org/draft-0/bill/invoice" | ||
issue_date: "2023-07-10" | ||
series: "TEST" | ||
code: "00002" | ||
supplier: | ||
name: "ESCUELA KEMPER URGATE" | ||
ext: | ||
mx-cfdi-fiscal-regime: "601" | ||
tax_id: | ||
country: "MX" | ||
code: "EKU9003173C9" | ||
zone: "21000" | ||
customer: | ||
name: "UNIVERSIDAD ROBOTICA ESPAÑOLA" | ||
ext: | ||
mx-cfdi-fiscal-regime: "601" | ||
mx-cfdi-use: "G01" | ||
tax_id: | ||
country: "MX" | ||
code: "URE180429TM6" | ||
zone: "86991" | ||
lines: | ||
- quantity: "1" | ||
item: | ||
name: "Comisión servicio de monedero electrónico" | ||
price: "10.00" | ||
ext: | ||
mx-cfdi-prod-serv: "84141602" | ||
taxes: | ||
- cat: "VAT" | ||
rate: "standard" | ||
payment: | ||
terms: | ||
notes: "Condiciones de pago" | ||
instructions: | ||
key: "online+wallet" | ||
complements: | ||
- $schema: "https://gobl.org/draft-0/regimes/mx/food-vouchers-complement" | ||
employer_registration: "12345678901234567890" | ||
account_number: "0123456789" | ||
lines: | ||
- e_wallet_id: "ABC1234" | ||
issue_date_time: "2022-07-19T10:20:30" | ||
employee: | ||
tax_code: "JUFA7608212V6" | ||
curp: "JUFA760821MDFRRR00" | ||
name: "Adriana Juarez Fernández" | ||
social_security: "12345678901" | ||
amount: 10.123 | ||
- e_wallet_id: "BCD4321" | ||
issue_date_time: "2022-08-20T11:20:30" | ||
employee: | ||
tax_code: "KAHO641101B39" | ||
curp: "KAHO641101HDFRRR00" | ||
name: "Oscar Kala Haak" | ||
amount: 20.4 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
{ | ||
"$schema": "https://gobl.org/draft-0/envelope", | ||
"head": { | ||
"uuid": "8a51fd30-2a27-11ee-be56-0242ac120002", | ||
"dig": { | ||
"alg": "sha256", | ||
"val": "91514018429e4d46deb1f689658d361ae4870ac5e5aa76133018086fb923e31b" | ||
}, | ||
"draft": true | ||
}, | ||
"doc": { | ||
"$schema": "https://gobl.org/draft-0/bill/invoice", | ||
"type": "standard", | ||
"series": "TEST", | ||
"code": "00002", | ||
"issue_date": "2023-07-10", | ||
"currency": "MXN", | ||
"supplier": { | ||
"name": "ESCUELA KEMPER URGATE", | ||
"tax_id": { | ||
"country": "MX", | ||
"zone": "21000", | ||
"code": "EKU9003173C9" | ||
}, | ||
"ext": { | ||
"mx-cfdi-fiscal-regime": "601" | ||
} | ||
}, | ||
"customer": { | ||
"name": "UNIVERSIDAD ROBOTICA ESPAÑOLA", | ||
"tax_id": { | ||
"country": "MX", | ||
"zone": "86991", | ||
"code": "URE180429TM6" | ||
}, | ||
"ext": { | ||
"mx-cfdi-fiscal-regime": "601", | ||
"mx-cfdi-use": "G01" | ||
} | ||
}, | ||
"lines": [ | ||
{ | ||
"i": 1, | ||
"quantity": "1", | ||
"item": { | ||
"name": "Comisión servicio de monedero electrónico", | ||
"price": "10.00", | ||
"ext": { | ||
"mx-cfdi-prod-serv": "84141602" | ||
} | ||
}, | ||
"sum": "10.00", | ||
"taxes": [ | ||
{ | ||
"cat": "VAT", | ||
"rate": "standard", | ||
"percent": "16.0%" | ||
} | ||
], | ||
"total": "10.00" | ||
} | ||
], | ||
"payment": { | ||
"terms": { | ||
"notes": "Condiciones de pago" | ||
}, | ||
"instructions": { | ||
"key": "online+wallet" | ||
} | ||
}, | ||
"totals": { | ||
"sum": "10.00", | ||
"total": "10.00", | ||
"taxes": { | ||
"categories": [ | ||
{ | ||
"code": "VAT", | ||
"rates": [ | ||
{ | ||
"key": "standard", | ||
"base": "10.00", | ||
"percent": "16.0%", | ||
"amount": "1.60" | ||
} | ||
], | ||
"amount": "1.60" | ||
} | ||
], | ||
"sum": "1.60" | ||
}, | ||
"tax": "1.60", | ||
"total_with_tax": "11.60", | ||
"payable": "11.60" | ||
}, | ||
"complements": [ | ||
{ | ||
"$schema": "https://gobl.org/draft-0/regimes/mx/food-vouchers-complement", | ||
"employer_registration": "12345678901234567890", | ||
"account_number": "0123456789", | ||
"total": "30.52", | ||
"lines": [ | ||
{ | ||
"e_wallet_id": "ABC1234", | ||
"issue_date_time": "2022-07-19T10:20:30", | ||
"employee": { | ||
"tax_code": "JUFA7608212V6", | ||
"curp": "JUFA760821MDFRRR00", | ||
"name": "Adriana Juarez Fernández", | ||
"social_security": "12345678901" | ||
}, | ||
"amount": "10.12" | ||
}, | ||
{ | ||
"e_wallet_id": "BCD4321", | ||
"issue_date_time": "2022-08-20T11:20:30", | ||
"employee": { | ||
"tax_code": "KAHO641101B39", | ||
"curp": "KAHO641101HDFRRR00", | ||
"name": "Oscar Kala Haak" | ||
}, | ||
"amount": "20.40" | ||
} | ||
] | ||
} | ||
] | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
package mx | ||
|
||
import ( | ||
"regexp" | ||
|
||
"github.com/invopop/gobl/cal" | ||
"github.com/invopop/gobl/cbc" | ||
"github.com/invopop/gobl/num" | ||
"github.com/invopop/validation" | ||
) | ||
|
||
// Constants for the precision of the complement's amounts | ||
const ( | ||
FoodVouchersFinalPrecision = 2 | ||
) | ||
|
||
// Complement's Codes Patterns | ||
const ( | ||
CURPPattern = "^[A-Z][A,E,I,O,U,X][A-Z]{2}[0-9]{2}[0-1][0-9][0-3][0-9][M,H][A-Z]{2}[B,C,D,F,G,H,J,K,L,M,N,Ñ,P,Q,R,S,T,V,W,X,Y,Z]{3}[0-9,A-Z][0-9]$" | ||
SocialSecurityPattern = "^[0-9]{11}$" | ||
) | ||
|
||
// Complement's Codes Regexps | ||
var ( | ||
CURPRegexp = regexp.MustCompile(CURPPattern) | ||
SocialSecurityRegexp = regexp.MustCompile(SocialSecurityPattern) | ||
) | ||
|
||
// FoodVouchersComplement carries the data to produce a CFDI's "Complemento de | ||
// Vales de Despensa" (version 1.0) providing detailed information about food | ||
// vouchers issued by an e-wallet supplier to its customer's employees. | ||
// | ||
// This struct maps to the `ValesDeDespensa` root node in the CFDI's complement. | ||
type FoodVouchersComplement struct { | ||
// Customer's employer registration number (maps to `registroPatronal`). | ||
EmployerRegistration string `json:"employer_registration,omitempty" jsonschema:"title=Employer Registration"` | ||
// Customer's account number (maps to `numeroDeCuenta`). | ||
AccountNumber string `json:"account_number" jsonschema:"title=Account Number"` | ||
// Sum of all line amounts (calculated, maps to `total`). | ||
Total num.Amount `json:"total" jsonschema:"title=Total" jsonschema_extras:"calculated=true"` | ||
// List of food vouchers issued to the customer's employees (maps to `Conceptos`). | ||
Lines []*FoodVouchersLine `json:"lines" jsonschema:"title=Lines"` | ||
} | ||
|
||
// FoodVouchersLine represents a single food voucher issued to the e-wallet of | ||
// one of the customer's employees. It maps to one `Concepto` node in the CFDI's | ||
// complement. | ||
type FoodVouchersLine struct { | ||
// Identifier of the e-wallet that received the food voucher (maps to `Identificador`). | ||
EWalletID cbc.Code `json:"e_wallet_id" jsonschema:"title=E-wallet Identifier"` | ||
// Date and time of the food voucher's issue (maps to `Fecha`). | ||
IssueDateTime cal.DateTime `json:"issue_date_time" jsonschema:"title=Issue Date and Time"` | ||
// Employee that received the food voucher. | ||
Employee *FoodVouchersEmployee `json:"employee,omitempty" jsonschema:"title=Employee"` | ||
// Amount of the food voucher (maps to `importe`). | ||
Amount num.Amount `json:"amount" jsonschema:"title=Amount"` | ||
} | ||
|
||
// FoodVouchersEmployee represents an employee that received a food voucher. It | ||
// groups employee related field that appears under the `Concepto` node in the | ||
// CFDI's complement. | ||
type FoodVouchersEmployee struct { | ||
// Employee's tax identity code (maps to `rfc`). | ||
TaxCode cbc.Code `json:"tax_code" jsonschema:"title=Employee's Tax Identity Code"` | ||
// Employee's CURP ("Clave Única de Registro de Población", maps to `curp`). | ||
CURP cbc.Code `json:"curp" jsonschema:"title=Employee's CURP"` | ||
// Employee's name (maps to `nombre`). | ||
Name string `json:"name" jsonschema:"title=Employee's Name"` | ||
// Employee's Social Security Number (maps to `numSeguridadSocial`). | ||
SocialSecurity cbc.Code `json:"social_security,omitempty" jsonschema:"title=Employee's Social Security Number"` | ||
} | ||
|
||
// Validate checks the FoodVouchersComplement data according to the SAT's | ||
// rules for the "Complemento de Vales de Despensa". | ||
func (fvc *FoodVouchersComplement) Validate() error { | ||
return validation.ValidateStruct(fvc, | ||
validation.Field(&fvc.EmployerRegistration, validation.Length(0, 20)), | ||
validation.Field(&fvc.AccountNumber, | ||
validation.Required, | ||
validation.Length(0, 20), | ||
), | ||
validation.Field(&fvc.Total, validation.Required), | ||
validation.Field(&fvc.Lines, validation.Required), | ||
) | ||
} | ||
|
||
// Validate checks the FoodVouchersLine data is valid. | ||
func (fvl *FoodVouchersLine) Validate() error { | ||
return validation.ValidateStruct(fvl, | ||
validation.Field(&fvl.EWalletID, | ||
validation.Required, | ||
validation.Length(0, 20), | ||
), | ||
validation.Field(&fvl.IssueDateTime, cal.DateTimeNotZero()), | ||
validation.Field(&fvl.Employee, validation.Required), | ||
validation.Field(&fvl.Amount, validation.Required), | ||
) | ||
} | ||
|
||
// Validate checks the FoodVouchersEmployee data is valid. | ||
func (fve *FoodVouchersEmployee) Validate() error { | ||
return validation.ValidateStruct(fve, | ||
validation.Field(&fve.TaxCode, | ||
validation.Required, | ||
validation.By(validateTaxCode), | ||
), | ||
validation.Field(&fve.CURP, | ||
validation.Required, | ||
validation.Match(CURPRegexp), | ||
), | ||
validation.Field(&fve.Name, | ||
validation.Required, | ||
validation.Length(0, 100), | ||
), | ||
validation.Field(&fve.SocialSecurity, | ||
validation.Match(SocialSecurityRegexp), | ||
), | ||
) | ||
} | ||
|
||
// Calculate performs the complement's calculations and normalisations. | ||
func (fvc *FoodVouchersComplement) Calculate() error { | ||
fvc.Total = num.MakeAmount(0, FoodVouchersFinalPrecision) | ||
|
||
for _, l := range fvc.Lines { | ||
l.Amount = l.Amount.Rescale(FoodVouchersFinalPrecision) | ||
|
||
fvc.Total = fvc.Total.Add(l.Amount) | ||
} | ||
|
||
return nil | ||
} |
Oops, something went wrong.