Files
enterprise-addons/l10n_pe_reports/models/account_ple_reports.py
T

414 lines
20 KiB
Python

# Part of Odoo. See LICENSE file for full copyright and licensing details.
import csv
import re
from datetime import datetime
from io import StringIO
from odoo import _, api, models
from odoo.tools import DEFAULT_SERVER_DATE_FORMAT, SQL
from odoo.exceptions import UserError
class L10n_PeTaxPleReportHandler(models.AbstractModel):
_name = 'l10n_pe.tax.ple.report.handler'
_inherit = ["account.tax.report.handler"]
_description = "PLE Generic Report"
def _custom_options_initializer(self, report, options, previous_options):
super()._custom_options_initializer(report, options, previous_options=previous_options)
options.setdefault("buttons", []).append(
{
"name": _("TXT"),
"sequence": 30,
"action": "export_file",
"action_param": "export_to_txt",
"file_export_type": _("TXT"),
}
)
@api.model
def _get_serie_folio(self, number):
values = {"serie": "", "folio": ""}
number_matchs = [rn for rn in re.finditer("\\d+", number or "")]
if number_matchs:
last_number_match = number_matchs[-1]
values["serie"] = number[: last_number_match.start()].replace("-", "") or ""
values["folio"] = last_number_match.group() or ""
return values
def _get_ple_report_data(self, options, current_groupby):
def build_result(query_res_lines):
def build_result_dict(query_res_lines):
result = {
"document_type": None,
"date": None,
"customer_vat": None,
"customer": None,
"amount_total": None,
"invoice_date": None,
"base_exp": None,
"base_igv": None,
"tax_igv": None,
"base_exo": None,
"base_ina": None,
"tax_isc": None,
"base_ivap": None,
"tax_ivap": None,
"vat_icbper": None,
"vat_igv_g_ng": None,
"vat_igv_ng": None,
"base_free": None,
"vat_other": None,
"base_withholdings": None,
}
if current_groupby and query_res_lines:
sign_total = -1 if query_res_lines[0]["move_type"] in ("out_invoice", "out_refund") else 1
rate = (
1 / query_res_lines[0]["move_rate"]
if query_res_lines[0]["move_rate"]
else 1
)
refund = query_res_lines[0]["reversed_entry_name"]
result = {
"move_name": query_res_lines[0]["move_name"],
"invoice_date": query_res_lines[0]["invoice_date"],
"date": query_res_lines[0]["date"],
"date_due": query_res_lines[0]["invoice_date_due"],
"document_type": query_res_lines[0]["document_type"],
"partner_lit": query_res_lines[0]["partner_lit"],
"partner_lit_code": query_res_lines[0]["partner_lit_code"],
"id_number": query_res_lines[0]["partner_vat"],
"customer_vat": query_res_lines[0]["partner_vat"],
"customer": query_res_lines[0]["partner_name"],
"amount_total": query_res_lines[0]["amount_currency"] * (sign_total * -1),
"currency": query_res_lines[0]["currency_name"],
"rate": abs(rate),
"base_igv": (query_res_lines[0]["base_igv"] or 0) * sign_total,
"tax_igv": (query_res_lines[0]["vat_igv"] or 0) * sign_total,
"base_igv_g_ng": query_res_lines[0]["base_igv_g_ng"],
"vat_igv_g_ng": query_res_lines[0]["vat_igv_g_ng"],
"base_igv_ng": query_res_lines[0]["base_igv_ng"],
"vat_igv_ng": query_res_lines[0]["vat_igv_ng"],
"base_exo": (query_res_lines[0]["base_exo"] or 0) * sign_total,
"base_ina": (query_res_lines[0]["base_ina"] or 0) * sign_total,
"base_ivap": (query_res_lines[0]["vat_ivap"] or 0) * sign_total,
"base_exp": (query_res_lines[0]["base_exp"] or 0) * sign_total,
"tax_ivap": (query_res_lines[0]["vat_ivap"] or 0) * sign_total,
"vat_icbper": (query_res_lines[0]["vat_icbper"] or 0) * sign_total,
"tax_isc": (query_res_lines[0]["vat_isc"] or 0) * sign_total,
"base_free": query_res_lines[0]["base_free"],
"vat_other": query_res_lines[0]["vat_other"],
"base_withholdings": query_res_lines[0]["base_withholding"],
"detraction_date": query_res_lines[0]["detraction_date"],
"detraction_number": query_res_lines[0]["detraction_number"],
"emission_date_related": query_res_lines[0]["reversed_entry_date"]
if refund
else query_res_lines[0]["debit_origin_date"],
"document_type_related": query_res_lines[0]["reversed_entry_document_type"]
if refund
else query_res_lines[0]["debit_origin_document_type"],
"related_document": query_res_lines[0]["reversed_entry_name"]
if refund
else query_res_lines[0]["debit_origin_name"],
"status": query_res_lines[0]["state"],
"edi_state": query_res_lines[0]["edi_state"],
"invoice_dua_name": query_res_lines[0]["invoice_dua_name"],
"invoice_dua_document_type": query_res_lines[0]["invoice_dua_document_type"],
"invoice_dua_date": query_res_lines[0]["invoice_dua_date"],
"partner_country_code": query_res_lines[0]["partner_country_code"],
"partner_street": query_res_lines[0]["partner_street"],
"partner_country_agreement_code": query_res_lines[0]["partner_country_agreement_code"],
"usage_type_code": query_res_lines[0]["usage_type_code"],
"service_modality": query_res_lines[0]["service_modality"],
"company_vat": query_res_lines[0]["company_vat"],
"company_name": query_res_lines[0]["company_name"],
}
return result
if not current_groupby:
return build_result_dict(query_res_lines)
result = []
all_res_per_grouping_key = {}
for query_res in query_res_lines:
grouping_key = query_res[current_groupby]
all_res_per_grouping_key.setdefault(grouping_key, []).append(query_res)
for grouping_key, query_res_lines in all_res_per_grouping_key.items():
result.append((grouping_key, build_result_dict(query_res_lines)))
return result
if self.env.company.parent_id:
raise UserError(
_("When branches are used, it is necessary to access from the main company to generate the reports."))
report = self.env["account.report"].browse(options["report_id"])
query = report._get_report_query(options, 'strict_range')
if self.env.company.chart_template != 'pe':
return build_result([])
ref = self.env.ref
cid = self.env.company.id
try:
tax_group_igv = ref(f"account.{cid}_tax_group_igv").id
tax_group_igv_g_ng = ref(f"account.{cid}_tax_group_igv_g_ng").id
tax_group_igv_ng = ref(f"account.{cid}_tax_group_igv_ng").id
tax_group_exp = ref(f"account.{cid}_tax_group_exp").id
tax_group_exo = ref(f"account.{cid}_tax_group_exo").id
tax_group_ina = ref(f"account.{cid}_tax_group_ina").id
tax_group_ivap = ref(f"account.{cid}_tax_group_ivap").id
tax_group_icbper = ref(f"account.{cid}_tax_group_icbper").id
tax_group_isc = ref(f"account.{cid}_tax_group_isc").id
tax_group_gra = ref(f"account.{cid}_tax_group_gra").id
tax_group_other = ref(f"account.{cid}_tax_group_other").id
tax_group_ret = ref(f"account.{cid}_tax_group_ret").id
except ValueError:
raise UserError(_("In order to generate the PLE reports, please update l10n_pe module to update the required data."))
query = SQL(
"""
SELECT
account_move_line__move_id.id,
account_move_line__move_id.name as move_name,
account_move_line__move_id.ref as move_ref,
account_move_line__move_id.invoice_currency_rate as move_rate,
account_move_line__move_id.edi_state,
rp.name as partner_name,
rp.vat as partner_vat,
rp.street as partner_street,
lit.name->>'en_US' as partner_lit,
lit.l10n_pe_vat_code as partner_lit_code,
rp.country_id as partner_country_id,
rpc.l10n_pe_code as partner_country_code,
rpc.l10n_pe_agreement_code as partner_country_agreement_code,
account_move_line__move_id.currency_id,
rc.name as currency_name,
account_move_line__move_id__l10n_latam_document_type_id.code as document_type,
account_move_line__move_id.l10n_pe_detraction_date as detraction_date,
account_move_line__move_id.l10n_pe_detraction_number as detraction_number,
account_move_line__move_id.l10n_pe_service_modality as service_modality,
account_move_line__move_id.l10n_pe_usage_type_id as usage_type_id,
lprt.code as usage_type_code,
account_move_line__move_id.id as move_id,
account_move_line__move_id.move_type,
account_move_line__move_id.date,
account_move_line__move_id.invoice_date,
account_move_line__move_id.invoice_date_due,
account_move_line__move_id.partner_id,
account_move_line__move_id.journal_id,
account_move_line__move_id.name,
account_move_line__move_id.l10n_latam_document_type_id as l10n_latam_document_type_id,
account_move_line__move_id.state,
account_move_line__move_id.company_id,
dua.name as invoice_dua_name,
dua.invoice_date as invoice_dua_date,
dua.l10n_latam_document_type_id as invoice_dua_document_type_id,
ldt_dua.code as invoice_dua_document_type,
reversed_entry.name as reversed_entry_name,
reversed_entry.invoice_date as reversed_entry_date,
reversed_entry.l10n_latam_document_type_id as reversed_entry_document_type_id,
ldt_reversed_entry.code as reversed_entry_document_type,
debit_origin.name as debit_origin_name,
debit_origin.invoice_date as debit_origin_date,
debit_origin.l10n_latam_document_type_id as debit_origin_document_type_id,
ldt_debit_origin.code as debit_origin_document_type,
partner_company.vat AS company_vat,
company.name AS company_name,
sum(CASE WHEN btg.id = %(tax_group_igv)s
THEN account_move_line.balance ELSE Null END) as base_igv,
sum(CASE WHEN ntg.id = %(tax_group_igv)s
THEN account_move_line.balance ELSE Null END) as vat_igv,
sum(CASE WHEN btg.id = %(tax_group_igv_g_ng)s
THEN account_move_line.balance ELSE Null END) as base_igv_g_ng,
sum(CASE WHEN ntg.id = %(tax_group_igv_g_ng)s
THEN account_move_line.balance ELSE Null END) as vat_igv_g_ng,
sum(CASE WHEN btg.id = %(tax_group_igv_ng)s
THEN account_move_line.balance ELSE Null END) as base_igv_ng,
sum(CASE WHEN ntg.id = %(tax_group_igv_ng)s
THEN account_move_line.balance ELSE Null END) as vat_igv_ng,
sum(CASE WHEN btg.id = %(tax_group_exp)s
THEN account_move_line.balance ELSE Null END) as base_exp,
sum(CASE WHEN ntg.id = %(tax_group_exp)s
THEN account_move_line.balance ELSE Null END) as vat_exp,
sum(CASE WHEN btg.id = %(tax_group_exo)s
THEN account_move_line.balance ELSE Null END) as base_exo,
sum(CASE WHEN ntg.id = %(tax_group_exo)s
THEN account_move_line.balance ELSE Null END) as vat_exo,
sum(CASE WHEN btg.id = %(tax_group_ina)s
THEN account_move_line.balance ELSE Null END) as base_ina,
sum(CASE WHEN ntg.id = %(tax_group_ina)s
THEN account_move_line.balance ELSE Null END) as vat_ina,
sum(CASE WHEN btg.id = %(tax_group_ivap)s
THEN account_move_line.balance ELSE Null END) as base_ivap,
sum(CASE WHEN ntg.id = %(tax_group_ivap)s
THEN account_move_line.balance ELSE Null END) as vat_ivap,
sum(CASE WHEN btg.id = %(tax_group_icbper)s
THEN account_move_line.balance ELSE Null END) as base_icbper,
sum(CASE WHEN ntg.id = %(tax_group_icbper)s
THEN account_move_line.balance ELSE Null END) as vat_icbper,
sum(CASE WHEN btg.id = %(tax_group_isc)s
THEN account_move_line.balance ELSE Null END) as base_isc,
sum(CASE WHEN ntg.id = %(tax_group_isc)s
THEN account_move_line.balance ELSE Null END) as vat_isc,
sum(CASE WHEN btg.id = %(tax_group_gra)s
THEN account_move_line.balance ELSE Null END) as base_free,
sum(CASE WHEN ntg.id = %(tax_group_gra)s
THEN account_move_line.balance ELSE Null END) as vat_free,
sum(CASE WHEN btg.id = %(tax_group_other)s
THEN account_move_line.balance ELSE Null END) as base_other,
sum(CASE WHEN ntg.id = %(tax_group_other)s
THEN account_move_line.balance ELSE Null END) as vat_other,
sum(CASE WHEN btg.id = %(tax_group_ret)s
THEN account_move_line.balance ELSE Null END) as base_withholding,
sum(CASE WHEN ntg.id = %(tax_group_ret)s
THEN account_move_line.balance ELSE Null END) as vat_withholding,
account_move_line__move_id.amount_total_signed as amount_currency
FROM
account_move_line
LEFT JOIN
account_move as account_move_line__move_id
ON account_move_line.move_id = account_move_line__move_id.id
LEFT JOIN
-- nt = net tax
account_tax AS nt
ON account_move_line.tax_line_id = nt.id
LEFT JOIN
account_move_line_account_tax_rel AS account_move_linetr
ON account_move_line.id = account_move_linetr.account_move_line_id
LEFT JOIN
-- bt = base tax
account_tax AS bt
ON account_move_linetr.account_tax_id = bt.id
LEFT JOIN
account_tax_group AS btg
ON btg.id = bt.tax_group_id
LEFT JOIN
account_tax_group AS ntg
ON ntg.id = nt.tax_group_id
LEFT JOIN
res_partner AS rp
ON rp.id = account_move_line__move_id.commercial_partner_id
LEFT JOIN
res_country AS rpc
ON rpc.id = rp.country_id
LEFT JOIN
l10n_latam_identification_type AS lit
ON rp.l10n_latam_identification_type_id = lit.id
LEFT JOIN
res_currency AS rc
ON rc.id = account_move_line__move_id.currency_id
LEFT JOIN
l10n_latam_document_type AS account_move_line__move_id__l10n_latam_document_type_id
ON account_move_line__move_id__l10n_latam_document_type_id.id = account_move_line__move_id.l10n_latam_document_type_id
LEFT JOIN
account_move AS reversed_entry
ON reversed_entry.id = account_move_line__move_id.reversed_entry_id
LEFT JOIN
l10n_latam_document_type AS ldt_reversed_entry
ON ldt_reversed_entry.id = reversed_entry.l10n_latam_document_type_id
LEFT JOIN
account_move AS debit_origin
ON debit_origin.id = account_move_line__move_id.debit_origin_id
LEFT JOIN
l10n_latam_document_type AS ldt_debit_origin
ON ldt_debit_origin.id = debit_origin.l10n_latam_document_type_id
LEFT JOIN
account_move AS dua
ON dua.id = account_move_line__move_id.l10n_pe_dua_invoice_id
LEFT JOIN
l10n_latam_document_type AS ldt_dua
ON ldt_dua.id = dua.l10n_latam_document_type_id
LEFT JOIN
l10n_pe_ple_usage AS lprt
ON lprt.id = account_move_line__move_id.l10n_pe_usage_type_id
LEFT JOIN
account_journal AS aj
ON aj.id = account_move_line__move_id.journal_id
LEFT JOIN
res_company AS company
ON company.id = account_move_line__move_id.company_id
LEFT JOIN
res_partner AS partner_company
ON partner_company.id = company.partner_id
WHERE
%(search_condition)s
AND (account_move_line.tax_line_id is not null or btg.l10n_pe_edi_code is not null)
AND aj.l10n_latam_use_documents
GROUP BY
account_move_line__move_id.id, rp.id, lit.id, rc.id, account_move_line__move_id__l10n_latam_document_type_id.id,
reversed_entry.id, ldt_reversed_entry.id, debit_origin.id,
ldt_debit_origin.id, dua.id, ldt_dua.id, rpc.id, lprt.id, company.id, partner_company.id
ORDER BY
account_move_line__move_id.date, account_move_line__move_id.name
""",
tax_group_igv=tax_group_igv,
tax_group_igv_g_ng=tax_group_igv_g_ng,
tax_group_igv_ng=tax_group_igv_ng,
tax_group_exp=tax_group_exp,
tax_group_exo=tax_group_exo,
tax_group_ina=tax_group_ina,
tax_group_ivap=tax_group_ivap,
tax_group_icbper=tax_group_icbper,
tax_group_isc=tax_group_isc,
tax_group_gra=tax_group_gra,
tax_group_other=tax_group_other,
tax_group_ret=tax_group_ret,
search_condition=query.where_clause,
)
self.env.flush_all()
self.env.cr.execute(query)
query_res_lines = self.env.cr.dictfetchall()
return build_result(query_res_lines)
# TODO: To be deprecated
def _get_document_status(self, state, invoice_date, date):
"""From SUNAT documentation:
1. Required
2. Register '0' when the operation (optional annotation without effect in the IGV) corresponds to the period
3. Register '1' when the operation (taxed sales, exonerated, unaffected and/or exportation)
corresponds to the period, like the credit and debit notes emitted on the period.
4. Register '2' when the document has been disabled during the previous period to be delivered, emitted or
during the emission.
5. Register '6' when the emission date of the document payment o taxes payment, for operations
that give right to tax credit, is before to that annotation period and this is inside of the next twelve
months to the payment emission or tax payment, as appropriate.
6. Register '8' when the operation (taxed sales, exonerated, unaffected and/or exportations)
corresponds to the previous period and HAS NOT been registered in that period.
7. Register '9' when the operation (taxed sales, exonerated, unaffected and/or exportations)
corresponds to the previous period and HAS BEEN registered in that period."""
value = 0
if state == "cancel":
value = 2
elif state == "posted" and (invoice_date.month != date.month or invoice_date.year != date.year):
value = 6
elif state == "posted":
value = 1
return value
def _get_report_number(self):
return ""
def _get_file_txt(self, options, data):
txt_result = ""
if data:
output = StringIO()
writer = csv.DictWriter(output, delimiter="|", skipinitialspace=True, fieldnames=data[0].keys())
writer.writerows(data)
txt_result = output.getvalue()
# The name of the file is based on this link
# http://orientacion.sunat.gob.pe/index.php/empresas-menu/libros-y-registros-vinculados-asuntos-tributarios-empresas/sistema-de-libros-electronicos-ple/6560-05-nomenclatura-de-libros-electronicos
date = datetime.strptime(options["date"]["date_from"], DEFAULT_SERVER_DATE_FORMAT)
has_data = int(bool(data))
report_filename = "LE%s%s%02d00%s1%s12" % (
self.env.company.vat, date.year, date.month, self._get_report_number(), has_data)
return {
"file_name": report_filename,
"file_content": txt_result.encode(),
"file_type": "txt",
}