Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
What's new
10
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Open sidebar
l10n_mx
l10n_mx_facturae
Commits
bd46a96a
Commit
bd46a96a
authored
6 years ago
by
Cuauhtémoc Díaz Minor
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
style(black): code-analysis
parent
1dba7aa2
Pipeline
#5867
passed with stage
in 55 seconds
Changes
14
Pipelines
1
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
393 additions
and
422 deletions
+393
-422
l10n_mx_facturae/__openerp__.py
l10n_mx_facturae/__openerp__.py
+26
-27
l10n_mx_facturae/migrations/2.4.0/post-migration.py
l10n_mx_facturae/migrations/2.4.0/post-migration.py
+14
-11
l10n_mx_facturae/migrations/2.4.1/post-migration.py
l10n_mx_facturae/migrations/2.4.1/post-migration.py
+3
-4
l10n_mx_facturae/migrations/2.8.0/post-migration.py
l10n_mx_facturae/migrations/2.8.0/post-migration.py
+7
-7
l10n_mx_facturae/models/account_invoice.py
l10n_mx_facturae/models/account_invoice.py
+148
-153
l10n_mx_facturae/models/account_voucher.py
l10n_mx_facturae/models/account_voucher.py
+33
-39
l10n_mx_facturae/models/email_template.py
l10n_mx_facturae/models/email_template.py
+18
-23
l10n_mx_facturae/models/res_partner.py
l10n_mx_facturae/models/res_partner.py
+14
-12
l10n_mx_facturae/report/account_invoice.py
l10n_mx_facturae/report/account_invoice.py
+17
-17
l10n_mx_facturae/report/account_voucher.py
l10n_mx_facturae/report/account_voucher.py
+8
-8
l10n_mx_facturae/tests/radish/sign_invoice_test.py
l10n_mx_facturae/tests/radish/sign_invoice_test.py
+83
-96
l10n_mx_facturae/tests/radish/terrain.py
l10n_mx_facturae/tests/radish/terrain.py
+1
-1
l10n_mx_facturae/wizard/account_invoice_refund.py
l10n_mx_facturae/wizard/account_invoice_refund.py
+3
-3
setup.py
setup.py
+18
-21
No files found.
l10n_mx_facturae/__openerp__.py
View file @
bd46a96a
# -*- coding: utf-8 -*-
{
'
name
'
:
'
Factura Electronica CFDI
'
,
'
version
'
:
'
2.8.0
'
,
'
author
'
:
'
OpenPyme
'
,
'
category
'
:
'
Localization/Mexico
'
,
'
website
'
:
'
http://www.openpyme.mx/
'
,
'
license
'
:
'
AGPL-3
'
,
'
depends
'
:
[
'
account
'
,
"
name
"
:
"
Factura Electronica CFDI
"
,
"
version
"
:
"
2.8.0
"
,
"
author
"
:
"
OpenPyme
"
,
"
category
"
:
"
Localization/Mexico
"
,
"
website
"
:
"
http://www.openpyme.mx/
"
,
"
license
"
:
"
AGPL-3
"
,
"
depends
"
:
[
"
account
"
,
# TODO: Deprecate this module and include into account base
'
account_invoice_discount
'
,
'
base_vat
'
,
'
l10n_mx
'
,
'
l10n_mx_account_tax_category
'
,
'
l10n_mx_ir_attachment_facturae
'
,
'
l10n_mx_res_partner_bank
'
,
"
account_invoice_discount
"
,
"
base_vat
"
,
"
l10n_mx
"
,
"
l10n_mx_account_tax_category
"
,
"
l10n_mx_ir_attachment_facturae
"
,
"
l10n_mx_res_partner_bank
"
,
],
'demo'
:
[
"demo"
:
[],
"data"
:
[
"views/account_invoice.xml"
,
"views/account_voucher.xml"
,
"views/res_partner.xml"
,
"wizard/account_invoice_refund.xml"
,
"report/account_invoice.xml"
,
"report/account_voucher.xml"
,
"data/ir_cron.xml"
,
"data/email_template.xml"
,
"data/ir_attachment_facturae_config.xml"
,
],
'data'
:
[
'views/account_invoice.xml'
,
'views/account_voucher.xml'
,
'views/res_partner.xml'
,
'wizard/account_invoice_refund.xml'
,
'report/account_invoice.xml'
,
'report/account_voucher.xml'
,
'data/ir_cron.xml'
,
'data/email_template.xml'
,
'data/ir_attachment_facturae_config.xml'
,
],
'installable'
:
True
,
"installable"
:
True
,
}
This diff is collapsed.
Click to expand it.
l10n_mx_facturae/migrations/2.4.0/post-migration.py
View file @
bd46a96a
...
...
@@ -12,21 +12,22 @@ def migrate(env, installed_version):
'invoice' and set them as is_company True
"""
tools
.
message
(
env
.
cr
,
'l10n_mx_facturae'
,
'res.partner'
,
'is_company'
,
message
=
'Setting is_company on True for res.partners...'
,
env
.
cr
,
"l10n_mx_facturae"
,
"res.partner"
,
"is_company"
,
message
=
"Setting is_company on True for res.partners..."
,
)
partners
=
env
[
'
res.partner
'
].
search
([(
'
type
'
,
'='
,
'
invoice
'
)])
partners
.
write
({
'
is_company
'
:
True
})
partners
=
env
[
"
res.partner
"
].
search
([(
"
type
"
,
"="
,
"
invoice
"
)])
partners
.
write
({
"
is_company
"
:
True
})
# Delete removed view that reference to a field named rate
try
:
dummy
,
id
=
env
[
'ir.model.data'
].
get_object_reference
(
'l10n_mx_facturae'
,
'account_invoice_form_inh_l10n_mx_facturae_rate'
,
dummy
,
id
=
env
[
"ir.model.data"
].
get_object_reference
(
"l10n_mx_facturae"
,
"account_invoice_form_inh_l10n_mx_facturae_rate"
)
tools
.
logged_query
(
env
.
cr
,
'DELETE FROM ir_ui_view WHERE id=%(id)s'
,
args
=
{
'id'
:
id
},
env
.
cr
,
"DELETE FROM ir_ui_view WHERE id=%(id)s"
,
args
=
{
"id"
:
id
}
)
except
ValueError
:
# account_invoice_form_inh_l10n_mx_facturae_rate view not exist in DB
...
...
@@ -35,7 +36,8 @@ def migrate(env, installed_version):
# Delete views from obsolete modules and mark them as uninstalled
tools
.
logged_query
(
env
.
cr
,
"""
env
.
cr
,
"""
UPDATE ir_module_module SET state='uninstalled'
WHERE name in (
'l10n_mx_invoice_datetime', 'l10n_mx_notes_invoice',
...
...
@@ -51,7 +53,8 @@ def migrate(env, installed_version):
"""
,
)
tools
.
logged_query
(
env
.
cr
,
"""
env
.
cr
,
"""
DELETE from ir_ui_view v
USING ir_model_data d
WHERE v.id=d.res_id AND d.model='ir.ui.view' AND
...
...
This diff is collapsed.
Click to expand it.
l10n_mx_facturae/migrations/2.4.1/post-migration.py
View file @
bd46a96a
...
...
@@ -9,14 +9,13 @@ from openerp import SUPERUSER_ID
def
migrate
(
env
,
installed_version
):
"""Set a configuration for invoices emitted with CFDI 3.2 specification
so that the user can be able to cancel any invoice as needed"""
dummy
,
config_id
=
env
[
'ir.model.data'
].
get_object_reference
(
'l10n_mx_facturae'
,
'ir_attachment_facturae_mx_config_account_invoice_32'
,
dummy
,
config_id
=
env
[
"ir.model.data"
].
get_object_reference
(
"l10n_mx_facturae"
,
"ir_attachment_facturae_mx_config_account_invoice_32"
)
tools
.
logged_query
(
env
.
cr
,
"""UPDATE ir_attachment_facturae_mx SET config_id=%(config_id)s
WHERE cfdi_type='incoming' AND state IN ('signed', 'done')
AND config_id is null;"""
,
args
=
{
'
config_id
'
:
config_id
},
args
=
{
"
config_id
"
:
config_id
},
)
This diff is collapsed.
Click to expand it.
l10n_mx_facturae/migrations/2.8.0/post-migration.py
View file @
bd46a96a
...
...
@@ -12,16 +12,16 @@ _logger = logging.getLogger(__name__)
def
compute_cfdi_id
(
env
):
"""Update reference to `ir.attachment.facturae.mx` for invoices"""
ir_attachment_mx_obj
=
env
[
'
ir.attachment.facturae.mx
'
]
invoices
=
env
[
'
account.invoice
'
].
search
([(
'
state
'
,
'
in
'
,
[
'
open
'
,
'
paid
'
])])
_logger
.
info
(
'
Updating reference for cfdi on
'
+
str
(
len
(
invoices
))
+
'
invoices
'
)
ir_attachment_mx_obj
=
env
[
"
ir.attachment.facturae.mx
"
]
invoices
=
env
[
"
account.invoice
"
].
search
([(
"
state
"
,
"
in
"
,
[
"
open
"
,
"
paid
"
])])
_logger
.
info
(
"
Updating reference for cfdi on
"
+
str
(
len
(
invoices
))
+
"
invoices
"
)
for
invoice
in
invoices
:
related_attachment
=
ir_attachment_mx_obj
.
search
(
[
(
'
res_id
'
,
'='
,
invoice
.
id
),
(
'
type_attachment
'
,
'='
,
'
account.invoice
'
),
(
'
company_id
'
,
'='
,
invoice
.
company_id
.
id
),
(
'
state
'
,
'
in
'
,
[
'
signed
'
,
'
done
'
]),
(
"
res_id
"
,
"="
,
invoice
.
id
),
(
"
type_attachment
"
,
"="
,
"
account.invoice
"
),
(
"
company_id
"
,
"="
,
invoice
.
company_id
.
id
),
(
"
state
"
,
"
in
"
,
[
"
signed
"
,
"
done
"
]),
]
)
if
related_attachment
:
...
...
This diff is collapsed.
Click to expand it.
l10n_mx_facturae/models/account_invoice.py
View file @
bd46a96a
This diff is collapsed.
Click to expand it.
l10n_mx_facturae/models/account_voucher.py
View file @
bd46a96a
...
...
@@ -5,31 +5,28 @@ from openerp.tools import float_round
class
AccountVoucher
(
models
.
Model
):
_name
=
'account.voucher'
_inherit
=
[
'account.voucher'
,
'base.cfdi'
,
]
_name
=
"account.voucher"
_inherit
=
[
"account.voucher"
,
"base.cfdi"
]
state
=
fields
.
Selection
(
[
(
'
draft
'
,
'
Draft
'
),
(
'
cancel
'
,
'
Cancelled
'
),
(
'
proforma
'
,
'
Pro-forma
'
),
(
'
posted
'
,
'
Posted
'
),
(
'
signed
'
,
'
Signed
'
),
]
,
(
"
draft
"
,
"
Draft
"
),
(
"
cancel
"
,
"
Cancelled
"
),
(
"
proforma
"
,
"
Pro-forma
"
),
(
"
posted
"
,
"
Posted
"
),
(
"
signed
"
,
"
Signed
"
),
]
)
@
api
.
multi
def
sign_voucher
(
self
):
"""Create CFDI for selected vouchers"""
# Only vouchers to sign are the receipts
receipts
=
self
.
filtered
(
lambda
r
:
r
.
type
==
'
receipt
'
)
receipts
=
self
.
filtered
(
lambda
r
:
r
.
type
==
"
receipt
"
)
# Get only receipts that doesn't have a CFDI yet and create it
for
receipt
in
receipts
.
filtered
(
lambda
r
:
not
r
.
cfdi_id
.
exists
()):
receipt
.
create_cfdi
()
receipt
.
state
=
'
signed
'
receipt
.
state
=
"
signed
"
@
api
.
multi
def
cancel_voucher
(
self
):
...
...
@@ -44,10 +41,12 @@ class AccountVoucher(models.Model):
"""General steps needed for cancel a voucher"""
self
.
ensure_one
()
# Before cancel original voucher set relation to current related CFDI
self
.
write
({
'cfdi_relation_type'
:
self
.
env
.
ref
(
'l10n_mx.cfdi_relation_type_04'
).
id
,
'related_cfdi_ids'
:
[(
6
,
None
,
[
self
.
cfdi_id
.
id
])],
})
self
.
write
(
{
"cfdi_relation_type"
:
self
.
env
.
ref
(
"l10n_mx.cfdi_relation_type_04"
).
id
,
"related_cfdi_ids"
:
[(
6
,
None
,
[
self
.
cfdi_id
.
id
])],
}
)
# We also remove relation for attachments to avoid confusing situations
self
.
cfdi_id
.
file_xml_sign
.
res_id
=
None
self
.
cfdi_id
.
file_pdf
.
res_id
=
None
...
...
@@ -77,12 +76,11 @@ class AccountVoucher(models.Model):
# We need to give the date in the context to get proper rate
voucher_currency
=
self
.
currency_id
.
with_context
(
date
=
self
.
date
)
voucher_currency
=
voucher_currency
.
with_context
(
voucher_special_currency_rate
=
(
voucher_currency
.
rate
*
self
.
payment_rate
),
voucher_special_currency_rate
=
(
voucher_currency
.
rate
*
self
.
payment_rate
),
voucher_special_currency
=
(
self
.
payment_rate_currency_id
and
self
.
payment_rate_currency_id
.
id
or
False
self
.
payment_rate_currency_id
and
self
.
payment_rate_currency_id
.
id
or
False
),
)
return
voucher_currency
.
compute
(
1.0
,
self
.
company_id
.
currency_id
)
...
...
@@ -94,27 +92,24 @@ class AccountVoucher(models.Model):
# Get all records from account.move.line related to this voucher but
# affecting an account different than the selected for this voucher
move_lines
=
self
.
move_ids
.
filtered
(
lambda
x
:
x
.
account_id
!=
self
.
account_id
,
)
move_lines
=
self
.
move_ids
.
filtered
(
lambda
x
:
x
.
account_id
!=
self
.
account_id
)
# Get all lines form partial and full reconciliations
temp_lines
=
move_lines
.
mapped
(
'
reconcile_partial_id.line_partial_ids
'
)
temp_lines
|=
move_lines
.
mapped
(
'
reconcile_id.line_id
'
)
temp_lines
=
move_lines
.
mapped
(
"
reconcile_partial_id.line_partial_ids
"
)
temp_lines
|=
move_lines
.
mapped
(
"
reconcile_id.line_id
"
)
# Get only invoices related to this voucher
invoices
=
(
temp_lines
-
move_lines
).
mapped
(
'
invoice
'
)
invoices
=
invoices
.
filtered
(
lambda
i
:
i
.
type
==
'
out_invoice
'
)
invoices
=
(
temp_lines
-
move_lines
).
mapped
(
"
invoice
"
)
invoices
=
invoices
.
filtered
(
lambda
i
:
i
.
type
==
"
out_invoice
"
)
return
invoices
@
api
.
multi
def
numparcialidad
(
self
,
invoice
):
"""Computes payment number based on all payments done to invoice"""
# Get all payments done to given invoice
payments
=
invoice
.
mapped
(
'
payment_ids
'
)
-
self
.
move_ids
payments
=
invoice
.
mapped
(
"
payment_ids
"
)
-
self
.
move_ids
# Leave only payments done in cash or bank older than current
payments
=
payments
.
filtered
(
lambda
r
:
r
.
date
<
self
.
date
and
r
.
journal_id
.
type
in
(
'cash'
,
'bank'
)
lambda
r
:
r
.
date
<
self
.
date
and
r
.
journal_id
.
type
in
(
"cash"
,
"bank"
)
)
return
len
(
payments
)
+
1
...
...
@@ -126,7 +121,7 @@ class AccountVoucher(models.Model):
amount_residual
=
invoice
.
amount_total
# Get all payments done to given invoice
payments
=
invoice
.
mapped
(
'
payment_ids
'
)
-
self
.
move_ids
payments
=
invoice
.
mapped
(
"
payment_ids
"
)
-
self
.
move_ids
# Leave only payments done before (older than) the current one
payments
=
payments
.
filtered
(
lambda
r
:
r
.
date
<
self
.
date
)
...
...
@@ -148,7 +143,7 @@ class AccountVoucher(models.Model):
amount_paid
=
0.0
# Get payments done in this voucher for given invoice
payments
=
invoice
.
mapped
(
'
payment_ids
'
)
&
self
.
move_ids
payments
=
invoice
.
mapped
(
"
payment_ids
"
)
&
self
.
move_ids
for
payment
in
payments
:
# If currency_id exists it means we are dealing with a multi
...
...
@@ -176,12 +171,11 @@ class AccountVoucher(models.Model):
self
.
ensure_one
()
voucher_currency
=
self
.
currency_id
.
with_context
(
date
=
self
.
date
)
voucher_currency
=
voucher_currency
.
with_context
(
voucher_special_currency_rate
=
(
voucher_currency
.
rate
*
self
.
payment_rate
),
voucher_special_currency_rate
=
(
voucher_currency
.
rate
*
self
.
payment_rate
),
voucher_special_currency
=
(
self
.
payment_rate_currency_id
and
self
.
payment_rate_currency_id
.
id
or
False
self
.
payment_rate_currency_id
and
self
.
payment_rate_currency_id
.
id
or
False
),
)
value
=
voucher_currency
.
compute
(
1.0
,
invoice
.
currency_id
,
round
=
False
)
...
...
This diff is collapsed.
Click to expand it.
l10n_mx_facturae/models/email_template.py
View file @
bd46a96a
...
...
@@ -7,11 +7,12 @@ class EmailTemplate(models.Model):
""" Extend generic message composition wizard to auto attach invoice
related files when sending opening the Send Mail invoice button.
"""
_inherit
=
'email.template'
_inherit
=
"email.template"
@
api
.
cr_uid_id_context
def
generate_email_batch
(
self
,
cr
,
uid
,
template_id
,
res_ids
,
context
=
None
,
fields
=
None
,
self
,
cr
,
uid
,
template_id
,
res_ids
,
context
=
None
,
fields
=
None
):
"""
Generates an email from the template for given (model, res_id) pair.
...
...
@@ -24,27 +25,25 @@ class EmailTemplate(models.Model):
format expected by :py:meth:`mail_thread.message_post`.
"""
values
=
super
(
EmailTemplate
,
self
).
generate_email_batch
(
cr
,
uid
,
template_id
,
res_ids
,
context
=
context
,
fields
=
fields
,
cr
,
uid
,
template_id
,
res_ids
,
context
=
context
,
fields
=
fields
)
att_obj
=
self
.
pool
.
get
(
'
ir.attachment.facturae.mx
'
)
ir_model_data
=
self
.
pool
.
get
(
'
ir.model.data
'
)
att_obj
=
self
.
pool
.
get
(
"
ir.attachment.facturae.mx
"
)
ir_model_data
=
self
.
pool
.
get
(
"
ir.model.data
"
)
reference_ids
=
[]
states
=
[
'
done
'
]
states
=
[
"
done
"
]
data
=
{
'
account
'
:
'
email_template_edi_invoice
'
,
'
portal_sale
'
:
'
email_template_edi_invoice
'
,
'
l10n_mx_ir_attachment_facturae
'
:
'
email_template_template_facturae_mx
'
,
'
l10n_mx_facturae
'
:
'
account_voucher_cfdi_email_template
'
,
"
account
"
:
"
email_template_edi_invoice
"
,
"
portal_sale
"
:
"
email_template_edi_invoice
"
,
"
l10n_mx_ir_attachment_facturae
"
:
"
email_template_template_facturae_mx
"
,
"
l10n_mx_facturae
"
:
"
account_voucher_cfdi_email_template
"
,
}
# Look for possible templates for invoice and override
for
module
,
name
in
data
.
iteritems
():
try
:
reference_ids
.
append
(
ir_model_data
.
get_object_reference
(
cr
,
uid
,
module
,
name
,
)[
1
],
ir_model_data
.
get_object_reference
(
cr
,
uid
,
module
,
name
)[
1
]
)
except
ValueError
:
# Template not installed so we catch the error
...
...
@@ -53,13 +52,11 @@ class EmailTemplate(models.Model):
if
template_id
in
reference_ids
:
for
res_id
in
res_ids
:
model
=
values
[
res_id
][
'
model
'
]
model
=
values
[
res_id
][
"
model
"
]
iatt_ids
=
att_obj
.
search
(
cr
,
uid
,
[
(
'res_id'
,
'='
,
res_id
),
(
'type_attachment'
,
'='
,
model
),
],
cr
,
uid
,
[(
"res_id"
,
"="
,
res_id
),
(
"type_attachment"
,
"="
,
model
)],
context
=
context
,
)
for
iattach
in
att_obj
.
browse
(
cr
,
uid
,
iatt_ids
,
context
=
context
):
...
...
@@ -69,8 +66,6 @@ class EmailTemplate(models.Model):
attachments
.
append
(
iattach
.
file_xml_sign
.
id
)
# Attach PDF file to mesage
attachments
.
append
(
iattach
.
file_pdf
.
id
)
values
[
res_id
][
'attachments'
]
=
[]
values
[
res_id
][
'attachment_ids'
]
=
[
(
6
,
0
,
attachments
),
]
values
[
res_id
][
"attachments"
]
=
[]
values
[
res_id
][
"attachment_ids"
]
=
[(
6
,
0
,
attachments
)]
return
values
This diff is collapsed.
Click to expand it.
l10n_mx_facturae/models/res_partner.py
View file @
bd46a96a
...
...
@@ -4,26 +4,28 @@ from openerp import fields, models
class
ResPartner
(
models
.
Model
):
_inherit
=
'
res.partner
'
_inherit
=
"
res.partner
"
cfdi_use
=
fields
.
Many2one
(
'cfdi.use'
,
'CFDI use'
,
help
=
'Cfdi usage that will be used by default on this customer '
'invoices and credit notes'
,
"cfdi.use"
,
"CFDI use"
,
help
=
"Cfdi usage that will be used by default on this customer "
"invoices and credit notes"
,
)
cfdi_adenda
=
fields
.
Many2one
(
'cfdi.adenda'
,
'CFDI Adendas'
,
help
=
'This field allows adding a node or addendum to the invoice'
,
"cfdi.adenda"
,
"CFDI Adendas"
,
help
=
"This field allows adding a node or addendum to the invoice"
,
)
cfdi_adenda_code
=
fields
.
Char
(
related
=
'cfdi_adenda.code'
,
help
=
'Helper field to improve view management'
,
related
=
"cfdi_adenda.code"
,
help
=
"Helper field to improve view management"
)
payment_method_id
=
fields
.
Many2one
(
'cfdi.payment.method'
,
string
=
'Payment Method'
,
help
=
'Payment method associated with this partner according'
'to CFDI 3.3 catalog.'
,
"cfdi.payment.method"
,
string
=
"Payment Method"
,
help
=
"Payment method associated with this partner according"
"to CFDI 3.3 catalog."
,
)
supplier_number
=
fields
.
Char
(
help
=
'
Number or reference that the Client assigned to our company.
'
,
help
=
"
Number or reference that the Client assigned to our company.
"
)
This diff is collapsed.
Click to expand it.
l10n_mx_facturae/report/account_invoice.py
View file @
bd46a96a
...
...
@@ -6,25 +6,25 @@ from openerp.report import report_sxw
class
Parser
(
report_sxw
.
rml_parse
):
def
__init__
(
self
,
cr
,
uid
,
name
,
context
):
super
(
Parser
,
self
).
__init__
(
cr
,
uid
,
name
,
context
)
self
.
localcontext
.
update
({
'qrcode_string'
:
self
.
_qrcode_string
,
})
self
.
localcontext
.
update
({
"qrcode_string"
:
self
.
_qrcode_string
})
def
_qrcode_string
(
self
,
invoice
):
if
invoice
.
cfdi_id
.
sello
:
sello
=
invoice
.
cfdi_id
.
sello
[
-
8
:
len
(
invoice
.
cfdi_id
.
sello
)]
else
:
sello
=
''
return
''
.
join
([
'https://verificacfdi.facturaelectronica.sat.gob.mx/default.aspx'
'?id='
,
invoice
.
cfdi_id
.
uuid
and
invoice
.
cfdi_id
.
uuid
or
'N/A'
,
'&re='
,
invoice
.
company_id
.
partner_id
.
vat_split
,
'&rr='
,
invoice
.
partner_id
.
vat_split
,
'&tt='
,
str
(
invoice
.
total
),
'&fe='
,
sello
,
])
sello
=
""
return
""
.
join
(
[
"https://verificacfdi.facturaelectronica.sat.gob.mx/default.aspx"
"?id="
,
invoice
.
cfdi_id
.
uuid
and
invoice
.
cfdi_id
.
uuid
or
"N/A"
,
"&re="
,
invoice
.
company_id
.
partner_id
.
vat_split
,
"&rr="
,
invoice
.
partner_id
.
vat_split
,
"&tt="
,
str
(
invoice
.
total
),
"&fe="
,
sello
,
]
)
This diff is collapsed.
Click to expand it.
l10n_mx_facturae/report/account_voucher.py
View file @
bd46a96a
...
...
@@ -8,7 +8,7 @@ class Parser(report_sxw.rml_parse):
def
__init__
(
self
,
cr
,
uid
,
name
,
context
):
super
(
self
.
__class__
,
self
).
__init__
(
cr
,
uid
,
name
,
context
)
self
.
localcontext
.
update
({
'
datosqr
'
:
self
.
_datosqr
})
self
.
localcontext
.
update
({
"
datosqr
"
:
self
.
_datosqr
})
def
_datosqr
(
self
,
o
):
"""Genera el string con info para la elaboración del codigo qr"""
...
...
@@ -16,14 +16,14 @@ class Parser(report_sxw.rml_parse):
def
xstr
(
s
):
if
s
:
return
str
(
s
)
return
str
(
'
N/A
'
)
return
str
(
"
N/A
"
)
msg
=
'
https://verificacfdi.facturaelectronica.sat.gob.mx/default.aspx
'
msg
=
"
https://verificacfdi.facturaelectronica.sat.gob.mx/default.aspx
"
if
o
.
cfdi_id
.
sello
:
msg
=
msg
+
'
?id=
'
+
xstr
(
o
.
cfdi_id
.
uuid
)
msg
=
msg
+
'
&re=
'
+
xstr
(
o
.
company_id
.
partner_id
.
vat_split
)
msg
=
msg
+
'
&rr=
'
+
xstr
(
o
.
partner_id
.
vat_split
)
msg
=
msg
+
'
&tt=
'
+
xstr
(
str
(
o
.
amount
))
msg
=
msg
+
'
&fe=
'
+
xstr
(
o
.
cfdi_id
.
sello
[
-
8
:
len
(
o
.
cfdi_id
.
sello
)])
msg
=
msg
+
"
?id=
"
+
xstr
(
o
.
cfdi_id
.
uuid
)
msg
=
msg
+
"
&re=
"
+
xstr
(
o
.
company_id
.
partner_id
.
vat_split
)
msg
=
msg
+
"
&rr=
"
+
xstr
(
o
.
partner_id
.
vat_split
)
msg
=
msg
+
"
&tt=
"
+
xstr
(
str
(
o
.
amount
))
msg
=
msg
+
"
&fe=
"
+
xstr
(
o
.
cfdi_id
.
sello
[
-
8
:
len
(
o
.
cfdi_id
.
sello
)])
return
msg
This diff is collapsed.
Click to expand it.
l10n_mx_facturae/tests/radish/sign_invoice_test.py
View file @
bd46a96a
...
...
@@ -22,72 +22,67 @@ def setup(scenario):
scenario
.
context
.
user
=
add_user
(
scenario
)
@
given
(
'
I have a customer
'
)
@
given
(
"
I have a customer
"
)
def
have_customer
(
step
):
partner_obj
=
step
.
context
.
env
[
'
res.partner
'
]
state_obj
=
step
.
context
.
env
[
'
res.country.state
'
]
country_obj
=
step
.
context
.
env
[
'
res.country
'
]
partner_obj
=
step
.
context
.
env
[
"
res.partner
"
]
state_obj
=
step
.
context
.
env
[
"
res.country.state
"
]
country_obj
=
step
.
context
.
env
[
"
res.country
"
]
partner
=
step
.
table
[
0
]
user_id
=
step
.
context
.
user
.
id
# It would be great to find a way to replace the list for
# a dictionary so that avoid list access from index
values
=
{
'
name
'
:
partner
[
0
],
'
street
'
:
partner
[
1
],
'
10n_mx_street3
'
:
partner
[
2
],
'
l10n_mx_city2
'
:
partner
[
3
],
'
city
'
:
partner
[
4
],
'
zip
'
:
partner
[
6
],
'
type
'
:
partner
[
8
],
"
name
"
:
partner
[
0
],
"
street
"
:
partner
[
1
],
"
10n_mx_street3
"
:
partner
[
2
],
"
l10n_mx_city2
"
:
partner
[
3
],
"
city
"
:
partner
[
4
],
"
zip
"
:
partner
[
6
],
"
type
"
:
partner
[
8
],
}
state_name
=
partner
[
5
]
country_name
=
partner
[
7
]
# Review if there is a country with the given name
# if not create it
country
=
country_obj
.
sudo
(
user_id
).
search
(
[(
'name'
,
'='
,
country_name
)],
limit
=
1
,
)
country
=
country_obj
.
sudo
(
user_id
).
search
([(
"name"
,
"="
,
country_name
)],
limit
=
1
)
if
country
:
country_id
=
country
[
0
].
id
values
.
update
({
'
country_id
'
:
country_id
})
values
.
update
({
"
country_id
"
:
country_id
})
else
:
country_values
=
{
'
name
'
:
country_name
}
country_values
=
{
"
name
"
:
country_name
}
country_id
=
country_obj
.
sudo
(
user_id
).
create
(
country_values
).
id
values
.
update
({
'
country_id
'
:
country_id
})
values
.
update
({
"
country_id
"
:
country_id
})
# Review if there is a state with the given name and the given country
# if not create it
state
=
state_obj
.
sudo
(
user_id
).
search
(
[
(
'name'
,
'='
,
state_name
),
(
'country_id'
,
'='
,
country_id
),
],
limit
=
1
,
[(
"name"
,
"="
,
state_name
),
(
"country_id"
,
"="
,
country_id
)],
limit
=
1
)
if
state
:
values
.
update
({
'
state_id
'
:
state
[
0
].
id
})
values
.
update
({
"
state_id
"
:
state
[
0
].
id
})
else
:
state_values
=
{
'
name
'
:
state_name
,
'
code
'
:
state_name
,
'
country_id
'
:
country_id
,
"
name
"
:
state_name
,
"
code
"
:
state_name
,
"
country_id
"
:
country_id
,
}
state_id
=
state_obj
.
sudo
(
user_id
).
create
(
state_values
).
id
values
.
update
({
'
state_id
'
:
state_id
})
values
.
update
({
"
state_id
"
:
state_id
})
# Create customer
partner
=
partner_obj
.
sudo
(
user_id
).
create
(
values
)
# Add partner id in step context
step
.
context
.
partner
=
partner
@
given
(
'
customer VAT number is MXAAA010101AAA
'
)
@
given
(
"
customer VAT number is MXAAA010101AAA
"
)
def
have_customer_vat
(
step
):
partner
=
step
.
context
.
partner
# Set vat number according to our step
partner
.
vat
=
'
MXBBB010101AAA
'
partner
.
vat
=
"
MXBBB010101AAA
"
@
when
(
'
I create a sales invoice for customer
'
)
@
when
(
"
I create a sales invoice for customer
"
)
def
create_invoice
(
step
):
account_invoice_obj
=
step
.
context
.
env
[
'
account.invoice
'
]
account_invoice_obj
=
step
.
context
.
env
[
"
account.invoice
"
]
partner_id
=
step
.
context
.
partner
.
id
company_id
=
step
.
context
.
company
.
id
account_id
=
step
.
context
.
account
.
id
...
...
@@ -95,49 +90,50 @@ def create_invoice(step):
user_id
=
step
.
context
.
user
.
id
# Create invoice with given values
values
=
{
'
partner_id
'
:
partner_id
,
'
account_id
'
:
account_id
,
'
journal_id
'
:
journal_id
,
'
company_id
'
:
company_id
,
'
state
'
:
'
draft
'
,
"
partner_id
"
:
partner_id
,
"
account_id
"
:
account_id
,
"
journal_id
"
:
journal_id
,
"
company_id
"
:
company_id
,
"
state
"
:
"
draft
"
,
}
invoice
=
account_invoice_obj
.
sudo
(
user_id
).
create
(
values
)
step
.
context
.
invoice
=
invoice
@
when
(
'
invoice line is
'
)
@
when
(
"
invoice line is
"
)
def
have_lines
(
step
):
invoice_line_obj
=
step
.
context
.
env
[
'
account.invoice.line
'
]
invoice_line_obj
=
step
.
context
.
env
[
"
account.invoice.line
"
]
invoice_lines
=
step
.
table
[
0
]
invoice_id
=
step
.
context
.
invoice
.
id
user_id
=
step
.
context
.
user
.
id
values
=
{
'
name
'
:
invoice_lines
[
0
],
'
quantity
'
:
invoice_lines
[
1
],
'
price_unit
'
:
invoice_lines
[
2
],
'
price_subtotal
'
:
invoice_lines
[
3
],
'
invoice_id
'
:
invoice_id
,
"
name
"
:
invoice_lines
[
0
],
"
quantity
"
:
invoice_lines
[
1
],
"
price_unit
"
:
invoice_lines
[
2
],
"
price_subtotal
"
:
invoice_lines
[
3
],
"
invoice_id
"
:
invoice_id
,
}
# Create lines
invoice_line_obj
.
sudo
(
user_id
).
create
(
values
)
@
when
(
'
validate invoice
'
)
@
when
(
"
validate invoice
"
)
def
validate_invoice
(
step
):
invoice
=
step
.
context
.
invoice
invoice
.
invoice_open
()
@
then
(
'
I expect to have a XML and a PDF with same name, and a UUID
'
)
@
then
(
"
I expect to have a XML and a PDF with same name, and a UUID
"
)
def
result
(
step
):
attachment_obj
=
step
.
context
.
env
[
'
ir.attachment
'
]
attachment_obj
=
step
.
context
.
env
[
"
ir.attachment
"
]
invoice
=
step
.
context
.
invoice
# Looking for attachments related with the invoice
attachments
=
attachment_obj
.
search
(
[(
'res_model'
,
'='
,
'account.invoice'
),
(
'res_id'
,
'='
,
invoice
.
id
)])
[(
"res_model"
,
"="
,
"account.invoice"
),
(
"res_id"
,
"="
,
invoice
.
id
)]
)
# We want to have at least two files being pdf and xml their extension
# Using regular expresions
file_types
=
[
'
.*\.pdf
'
,
'
.*\.xml
'
]
file_types
=
[
"
.*\.pdf
"
,
"
.*\.xml
"
]
for
file_type
in
file_types
:
pattern
=
re
.
compile
(
file_type
)
for
index
,
attachment
in
enumerate
(
attachments
):
...
...
@@ -145,26 +141,25 @@ def result(step):
break
assert
(
(
index
+
1
)
!=
len
(
attachments
),
'
{type} file does not exist
'
.
format
(
type
=
re
.
sub
(
'
.*\.
'
,
''
,
file_type
).
upper
()
,
"
{type} file does not exist
"
.
format
(
type
=
re
.
sub
(
"
.*\.
"
,
""
,
file_type
).
upper
()
),
)
# Test attachments have the same name
names
=
set
([
os
.
path
.
splitext
(
x
.
name
)[
0
]
for
x
in
attachments
])
assert
len
(
names
)
==
1
,
''
.
join
(
[
'Name files must be the same '
,
str
(
names
)])
assert
invoice
.
cfdi_folio_fiscal
,
'Invoice without UUID'
assert
len
(
names
)
==
1
,
""
.
join
([
"Name files must be the same "
,
str
(
names
)])
assert
invoice
.
cfdi_folio_fiscal
,
"Invoice without UUID"
def
add_company
(
scenario
):
company_obj
=
scenario
.
context
.
env
[
'
res.company
'
]
state_obj
=
scenario
.
context
.
env
[
'
res.country.state
'
]
country_obj
=
scenario
.
context
.
env
[
'
res.country
'
]
company_obj
=
scenario
.
context
.
env
[
"
res.company
"
]
state_obj
=
scenario
.
context
.
env
[
"
res.country.state
"
]
country_obj
=
scenario
.
context
.
env
[
"
res.country
"
]
# First of all looking for an existing company
company
=
company_obj
.
search
([],
limit
=
1
)
if
company
:
# If there is a company only change vat an return
company
.
vat
=
'
MXLAN7008173R5
'
company
.
vat
=
"
MXLAN7008173R5
"
return
company
# If there is not already a company create one
company_values
=
"""
...
...
@@ -172,64 +167,62 @@ def add_company(scenario):
l10n_mx_city2=Hacienda Del Rosario, city=Puebla, state=Puebla,
zip=72000, country=Mexico, vat=MXAAA010101AAA
"""
dict_company_values
=
dict
(
x
.
split
(
'='
)
for
x
in
company_values
.
split
(
'
,
'
))
# noqa
dict_company_values
=
dict
(
x
.
split
(
"="
)
for
x
in
company_values
.
split
(
"
,
"
))
# noqa
# Review if there is a country with the given name
# if not create it
country
=
country_obj
.
search
(
[(
'
name
'
,
'='
,
dict_company_values
[
'
country
'
])],
limit
=
1
,
[(
"
name
"
,
"="
,
dict_company_values
[
"
country
"
])],
limit
=
1
)
if
country
:
country_id
=
country
[
0
].
id
dict_company_values
.
update
({
'
country_id
'
:
country_id
})
dict_company_values
.
update
({
"
country_id
"
:
country_id
})
else
:
country_values
=
{
'
name
'
:
dict_company_values
[
'
country
'
]}
country_values
=
{
"
name
"
:
dict_company_values
[
"
country
"
]}
country_id
=
country_obj
.
create
(
country_values
).
id
dict_company_values
.
update
({
'
country_id
'
:
country_id
})
dict_company_values
.
update
({
"
country_id
"
:
country_id
})
# Review if there is a state with the given name and the given country
# if not create it
state
=
state_obj
.
search
(
[
(
'name'
,
'='
,
dict_company_values
[
'state'
]),
(
'country_id'
,
'='
,
country_id
),
],
limit
=
1
,
[(
"name"
,
"="
,
dict_company_values
[
"state"
]),
(
"country_id"
,
"="
,
country_id
)],
limit
=
1
,
)
if
state
:
dict_company_values
.
update
({
'
state_id
'
:
state
[
0
].
id
})
dict_company_values
.
update
({
"
state_id
"
:
state
[
0
].
id
})
else
:
state_values
=
{
'
name
'
:
dict_company_values
[
'
state
'
],
'
code
'
:
dict_company_values
[
'
state
'
],
'
country_id
'
:
country_id
,
"
name
"
:
dict_company_values
[
"
state
"
],
"
code
"
:
dict_company_values
[
"
state
"
],
"
country_id
"
:
country_id
,
}
state_id
=
state_obj
.
create
(
state_values
).
id
dict_company_values
.
update
({
'
state_id
'
:
state_id
})
dict_company_values
.
update
({
"
state_id
"
:
state_id
})
# Create company
company
=
company_obj
.
create
(
dict_company_values
)
return
company
def
set_test_certificates
(
scenario
):
certificate_obj
=
scenario
.
context
.
env
[
'
res.company.facturae.certificate
'
]
certificate_obj
=
scenario
.
context
.
env
[
"
res.company.facturae.certificate
"
]
# I hope cer and key files are placed in the same basedir
name_cer
=
'
CSD_Pruebas_CFDI_LAN7008173R5.cer
'
name_key
=
'
CSD_Pruebas_CFDI_LAN7008173R5.key
'
name_cer
=
"
CSD_Pruebas_CFDI_LAN7008173R5.cer
"
name_key
=
"
CSD_Pruebas_CFDI_LAN7008173R5.key
"
path_cer
=
os
.
path
.
join
(
os
.
path
.
dirname
(
__file__
),
name_cer
)
path_key
=
os
.
path
.
join
(
os
.
path
.
dirname
(
__file__
),
name_key
)
certificate_password
=
'
12345678a
'
with
open
(
path_cer
,
'
rb
'
)
as
file_cer
:
certificate_password
=
"
12345678a
"
with
open
(
path_cer
,
"
rb
"
)
as
file_cer
:
cetificate_file
=
base64
.
b64encode
(
file_cer
.
read
())
with
open
(
path_key
,
'
rb
'
)
as
file_key
:
with
open
(
path_key
,
"
rb
"
)
as
file_key
:
certificate_key_file
=
base64
.
b64encode
(
file_key
.
read
())
# Search for an existing compampany certicate
# or create one
company_id
=
scenario
.
context
.
company
.
id
certificate
=
certificate_obj
.
search
([(
'
company_id
'
,
'='
,
company_id
)])
certificate
=
certificate_obj
.
search
([(
"
company_id
"
,
"="
,
company_id
)])
if
not
certificate
:
values
=
{
'
company_id
'
:
company_id
,
'
cetificate_file
'
:
cetificate_file
,
'
certificate_key_file
'
:
certificate_key_file
,
'
certificate_password
'
:
certificate_password
,
"
company_id
"
:
company_id
,
"
cetificate_file
"
:
cetificate_file
,
"
certificate_key_file
"
:
certificate_key_file
,
"
certificate_password
"
:
certificate_password
,
}
certificate
=
certificate_obj
.
create
(
values
)
else
:
...
...
@@ -241,32 +234,26 @@ def set_test_certificates(scenario):
def
add_journal
(
scenario
):
account_journal_obj
=
scenario
.
context
.
env
[
'
account.journal
'
]
journal
=
account_journal_obj
.
search
([(
'
type
'
,
'='
,
'
sale
'
)],
limit
=
1
)
account_journal_obj
=
scenario
.
context
.
env
[
"
account.journal
"
]
journal
=
account_journal_obj
.
search
([(
"
type
"
,
"="
,
"
sale
"
)],
limit
=
1
)
return
journal
def
add_account
(
scenario
):
account_account_obj
=
scenario
.
context
.
env
[
'
account.account
'
]
account_account_obj
=
scenario
.
context
.
env
[
"
account.account
"
]
company_id
=
scenario
.
context
.
company
.
id
account
=
account_account_obj
.
search
(
[(
'company_id'
,
'='
,
company_id
),
(
'type'
,
'='
,
'receivable'
)],
limit
=
1
,
[(
"company_id"
,
"="
,
company_id
),
(
"type"
,
"="
,
"receivable"
)],
limit
=
1
)[
0
]
return
account
def
add_user
(
scenario
):
res_users_obj
=
scenario
.
context
.
env
[
'res.users'
]
group_obj
=
scenario
.
context
.
env
[
'res.groups'
]
group
=
group_obj
.
search
(
[(
'name'
,
'='
,
'Invoicing & Payments'
)],
limit
=
1
,
)
res_users_obj
=
scenario
.
context
.
env
[
"res.users"
]
group_obj
=
scenario
.
context
.
env
[
"res.groups"
]
group
=
group_obj
.
search
([(
"name"
,
"="
,
"Invoicing & Payments"
)],
limit
=
1
)
user
=
res_users_obj
.
search
(
[
(
'groups_id'
,
'in'
,
(
group
.
id
)),
(
'name'
,
'!='
,
'admin'
),
],
limit
=
1
,
[(
"groups_id"
,
"in"
,
(
group
.
id
)),
(
"name"
,
"!="
,
"admin"
)],
limit
=
1
)
assert
user
is
not
False
,
'
No user in Invoicing & Payments group
'
assert
user
is
not
False
,
"
No user in Invoicing & Payments group
"
return
user
This diff is collapsed.
Click to expand it.
l10n_mx_facturae/tests/radish/terrain.py
View file @
bd46a96a
...
...
@@ -16,7 +16,7 @@ def connect_database(scenario):
scenario
.
context
.
uid
=
openerp
.
SUPERUSER_ID
#: :class:`~openerp.api.Environment` for the current test case
scenario
.
context
.
env
=
api
.
Environment
(
scenario
.
context
.
cr
,
scenario
.
context
.
uid
,
{}
,
scenario
.
context
.
cr
,
scenario
.
context
.
uid
,
{}
)
...
...
This diff is collapsed.
Click to expand it.
l10n_mx_facturae/wizard/account_invoice_refund.py
View file @
bd46a96a
...
...
@@ -4,13 +4,13 @@ from openerp import fields, models
class
AccountInvoiceRefund
(
models
.
TransientModel
):
_inherit
=
'
account.invoice.refund
'
_inherit
=
"
account.invoice.refund
"
cfdi_relation_type
=
fields
.
Many2one
(
'
cfdi.relation.type
'
,
'
CFDI Relation type
'
,
required
=
True
,
"
cfdi.relation.type
"
,
"
CFDI Relation type
"
,
required
=
True
)
def
compute_refund
(
self
,
mode
=
'
refund
'
):
def
compute_refund
(
self
,
mode
=
"
refund
"
):
"""Call super function added cfdi_relation_type value in context
to be able to use later when creating refund"""
self
=
self
.
with_context
(
cfdi_relation_type
=
self
.
cfdi_relation_type
.
id
)
...
...
This diff is collapsed.
Click to expand it.
setup.py
View file @
bd46a96a
...
...
@@ -6,7 +6,7 @@ import os
from
setuptools
import
find_packages
,
setup
MANIFEST_NAMES
=
(
'
__openerp__.py
'
,
'
__manifest__.py
'
,
'
__terp__.py
'
)
MANIFEST_NAMES
=
(
"
__openerp__.py
"
,
"
__manifest__.py
"
,
"
__terp__.py
"
)
class
NoManifestFound
(
Exception
):
...
...
@@ -27,39 +27,36 @@ def parse_manifest(s):
def
read_manifest
(
addon_dir
):
manifest_path
=
get_manifest_path
(
addon_dir
)
if
not
manifest_path
:
raise
NoManifestFound
(
'
No manifest found in %s
'
%
addon_dir
)
raise
NoManifestFound
(
"
No manifest found in %s
"
%
addon_dir
)
return
parse_manifest
(
open
(
manifest_path
).
read
())
addon_dir
=
'
l10n_mx_facturae
'
addon_dir
=
"
l10n_mx_facturae
"
manifest
=
read_manifest
(
addon_dir
)
long_description
=
(
open
(
os
.
path
.
join
(
addon_dir
,
'README.rst'
)).
read
()
+
'
\n
'
+
'Contributors
\n
'
'============
\n
'
+
'
\n
'
+
open
(
os
.
path
.
join
(
addon_dir
,
'CONTRIBUTORS.rst'
)).
read
()
+
'
\n
'
+
open
(
os
.
path
.
join
(
addon_dir
,
'CHANGES.md'
)).
read
()
+
'
\n
'
)
open
(
os
.
path
.
join
(
addon_dir
,
"README.rst"
)).
read
()
+
"
\n
"
+
"Contributors
\n
"
"============
\n
"
+
"
\n
"
+
open
(
os
.
path
.
join
(
addon_dir
,
"CONTRIBUTORS.rst"
)).
read
()
+
"
\n
"
+
open
(
os
.
path
.
join
(
addon_dir
,
"CHANGES.md"
)).
read
()
+
"
\n
"
)
setup
(
name
=
addon_dir
,
version
=
manifest
.
get
(
'
version
'
),
version
=
manifest
.
get
(
"
version
"
),
long_description
=
long_description
,
# Get more from http://pypi.python.org/pypi?%3Aaction=list_classifiers
classifiers
=
[
'
Programming Language :: Python
'
,
'
Operating System :: OS Independent
'
,
'
Programming Language :: Python :: 2.7
'
,
"
Programming Language :: Python
"
,
"
Operating System :: OS Independent
"
,
"
Programming Language :: Python :: 2.7
"
,
],
keywords
=
'
Python PyERP
'
,
license
=
manifest
.
get
(
'
license
'
,
'
GPL-3
'
),
keywords
=
"
Python PyERP
"
,
license
=
manifest
.
get
(
"
license
"
,
"
GPL-3
"
),
packages
=
find_packages
(),
include_package_data
=
True
,
zip_safe
=
False
,
install_requires
=
[
'setuptools'
,
],
install_requires
=
[
"setuptools"
],
)
This diff is collapsed.
Click to expand it.
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment