Skip to content

Commit

Permalink
Merge pull request #211 from invopop/food-vouchers
Browse files Browse the repository at this point in the history
Food vouchers complement in MX
  • Loading branch information
cavalle authored Oct 4, 2023
2 parents 23590f0 + 19be3ea commit c8c959d
Show file tree
Hide file tree
Showing 5 changed files with 403 additions and 1 deletion.
56 changes: 56 additions & 0 deletions regimes/mx/examples/food-vouchers-complement.yaml
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
127 changes: 127 additions & 0 deletions regimes/mx/examples/out/food-vouchers-complement.json
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"
}
]
}
]
}
}
132 changes: 132 additions & 0 deletions regimes/mx/food_vouchers_complement.go
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
}
Loading

0 comments on commit c8c959d

Please sign in to comment.