From 39a490e673664d4cbee22ce109453616de4d0a4a Mon Sep 17 00:00:00 2001 From: Eneldo Serrata Date: Sun, 15 Nov 2015 11:59:05 -0400 Subject: [PATCH] ncf_manager first commit --- __init__.py | 3 + __openerp__.py | 43 +++ controllers.py | 20 + models/__init__.py | 10 + models/account.py | 47 +++ models/account_invoice.py | 117 ++++++ models/account_invoice_state.py | 37 ++ models/dgii_cancel.py | 133 +++++++ models/dgii_exterior.py | 173 +++++++++ models/dgii_purchase.py | 224 +++++++++++ models/dgii_sale.py | 193 +++++++++ models/res_partner.py | 9 + models/shop.py | 40 ++ models/tools.py | 558 +++++++++++++++++++++++++++ security/ir.model.access.csv | 2 + static/description/icon.paint | Bin 0 -> 354513 bytes static/description/icon.png | Bin 0 -> 11234 bytes templates.xml | 22 ++ views/account_invoice_state_view.xml | 22 ++ views/account_invoice_view.xml | 92 +++++ views/account_view.xml | 48 +++ views/dgii_cancel_view.xml | 78 ++++ views/dgii_exterior_view.xml | 84 ++++ views/dgii_purchase_view.xml | 90 +++++ views/dgii_sale_view.xml | 85 ++++ views/partner_view.xml | 26 ++ views/shop_view.xml | 51 +++ 27 files changed, 2207 insertions(+) create mode 100644 __init__.py create mode 100644 __openerp__.py create mode 100644 controllers.py create mode 100644 models/__init__.py create mode 100644 models/account.py create mode 100644 models/account_invoice.py create mode 100644 models/account_invoice_state.py create mode 100644 models/dgii_cancel.py create mode 100644 models/dgii_exterior.py create mode 100644 models/dgii_purchase.py create mode 100644 models/dgii_sale.py create mode 100644 models/res_partner.py create mode 100644 models/shop.py create mode 100644 models/tools.py create mode 100644 security/ir.model.access.csv create mode 100644 static/description/icon.paint create mode 100644 static/description/icon.png create mode 100644 templates.xml create mode 100644 views/account_invoice_state_view.xml create mode 100644 views/account_invoice_view.xml create mode 100644 views/account_view.xml create mode 100644 views/dgii_cancel_view.xml create mode 100644 views/dgii_exterior_view.xml create mode 100644 views/dgii_purchase_view.xml create mode 100644 views/dgii_sale_view.xml create mode 100644 views/partner_view.xml create mode 100644 views/shop_view.xml diff --git a/__init__.py b/__init__.py new file mode 100644 index 000000000..c7a6ca61f --- /dev/null +++ b/__init__.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- +import controllers +import models \ No newline at end of file diff --git a/__openerp__.py b/__openerp__.py new file mode 100644 index 000000000..e6ebd24c4 --- /dev/null +++ b/__openerp__.py @@ -0,0 +1,43 @@ +# -*- coding: utf-8 -*- +{ + 'name': "ncf_manager", + + 'summary': """ + Short (1 phrase/line) summary of the module's purpose, used as + subtitle on modules listing or apps.openerp.com""", + + 'description': """ + Long description of module's purpose + """, + + 'author': "My Company", + 'website': "http://www.yourcompany.com", + + # Categories can be used to filter modules in modules listing + # Check https://github.com/odoo/odoo/blob/master/openerp/addons/base/module/module_data.xml + # for the full list + 'category': 'Uncategorized', + 'version': '0.1', + + # any module necessary for this one to work correctly + 'depends': ['base','account'], + + # always loaded + 'data': [ + # 'security/ir.model.access.csv', + 'templates.xml', + 'views/partner_view.xml', + 'views/shop_view.xml', + 'views/account_view.xml', + 'views/account_invoice_view.xml', + 'views/dgii_purchase_view.xml', + 'views/dgii_sale_view.xml', + 'views/dgii_cancel_view.xml', + 'views/dgii_exterior_view.xml', + 'views/account_invoice_state_view.xml' + ], + # only loaded in demonstration mode + 'demo': [ + 'demo.xml', + ], +} \ No newline at end of file diff --git a/controllers.py b/controllers.py new file mode 100644 index 000000000..084f1095d --- /dev/null +++ b/controllers.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +from openerp import http + +# class NcfManager(http.Controller): +# @http.route('/ncf_manager/ncf_manager/', auth='public') +# def index(self, **kw): +# return "Hello, world" + +# @http.route('/ncf_manager/ncf_manager/objects/', auth='public') +# def list(self, **kw): +# return http.request.render('ncf_manager.listing', { +# 'root': '/ncf_manager/ncf_manager', +# 'objects': http.request.env['ncf_manager.ncf_manager'].search([]), +# }) + +# @http.route('/ncf_manager/ncf_manager/objects//', auth='public') +# def object(self, obj, **kw): +# return http.request.render('ncf_manager.object', { +# 'object': obj +# }) \ No newline at end of file diff --git a/models/__init__.py b/models/__init__.py new file mode 100644 index 000000000..ad374bc13 --- /dev/null +++ b/models/__init__.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +import shop +import account_invoice +import account +import dgii_purchase +import dgii_sale +import res_partner +import account_invoice_state +import dgii_cancel +import dgii_exterior \ No newline at end of file diff --git a/models/account.py b/models/account.py new file mode 100644 index 000000000..15a7999ef --- /dev/null +++ b/models/account.py @@ -0,0 +1,47 @@ +# -*- coding: utf-8 -*- + + +from openerp import models, fields + + +class AccountJournal(models.Model): + _inherit = "account.journal" + + purchase_type = fields.Selection([("normal","Proveedor normal"), + ("minor", "Gasto menor"), + ("informal", "Proveedor informal"), + ("exterior", "Pagos al exterior") + ], + string="Tipo de compra", default="normal") + + +class AccountFiscalPosition(models.Model): + _inherit = 'account.fiscal.position' + + supplier = fields.Boolean("Para proveedores") + client_fiscal_type = fields.Selection([ + ("final", "Consumidor final"), + ("fiscal", "Para credito fiscal"), + ("gov", "Gubernamental"), + ("special", "Regimenes especiales")], string="Tipo de comprobante") + supplier_fiscal_type = fields.Selection([ + ('01', '01 - Gastos de personal'), + ('02', '02 - Gastos por trabajo, suministros y servicios'), + ('03', '03 - Arrendamientos'), + ('04', '04 - Gastos de Activos Fijos'), + ('05', u'05 - Gastos de Representación'), + ('06', '06 - Otras Deducciones Admitidas'), + ('07', '07 - Gastos Financieros'), + ('08', '08 - Gastos Extraordinarios'), + ('09', '09 - Compras y Gastos que forman parte del Costo de Venta'), + ('10', '10 - Adquisiciones de Activos'), + ('11', '11 - Gastos de Seguro') + ], string="Tipo de gasto") + journal_id = fields.Many2one("account.journal", string="Diario de compra", domain="[('type','=','purchase')]") + + +class AccountTax(models.Model): + _inherit = 'account.tax' + + purchase_tax_type = fields.Selection([('itbis','ITBIS Pagado'),('ritbis','ITBIS Retenido'),('isr','ISR Retenido')], + default="itbis", string="Tipo de impuesto de compra") \ No newline at end of file diff --git a/models/account_invoice.py b/models/account_invoice.py new file mode 100644 index 000000000..9301212ad --- /dev/null +++ b/models/account_invoice.py @@ -0,0 +1,117 @@ +# -*- coding: utf-8 -*- + +from openerp import models, fields, api, exceptions +import requests +from tools import is_ncf, _internet_on + + +class AccountInvoice(models.Model): + _inherit = "account.invoice" + + + anulation_type = fields.Selection([ + ("01", "DETERIORO DE FACTURA PRE-IMPRESA"), + ("02", "ERRORES DE IMPRESIÓN (FACTURA PRE-IMPRESA)"), + ("03", u"IMPRESIÓN DEFECTUOSA"), + ("04", "DUPLICIDAD DE FACTURA"), + ("05", "CORRECCIÓN DE LA INFORMACIÓN"), + ("06", "CAMBIO DE PRODUCTOS"), + ("07", "DEVOLUCIÓN DE PRODUCTOS"), + ("08", "OMISIÓN DE PRODUCTOS"), + ("09", "ERRORES EN SECUENCIA DE NCF") + ], string=u"Tipo de anulación", copy=False) + shop_id = fields.Many2one("shop.ncf.config", string="Sucursal", required=False, + domain=lambda s: s.env["shop.ncf.config"].get_user_shop_domain(), + # default=lambda s: s.env["shop.ncf.config"].get_default_shop() + ) + ncf = fields.Char("NCF", size=19, copy=False) + ncf_required = fields.Boolean() + + + _sql_constraints = [ + ('number_uniq', 'unique(number, company_id, journal_id, type, partner_id)', 'Invoice Number must be unique per Company!'), + ] + + + @api.onchange('journal_id') + def _onchange_journal_id(self): + if self.type in ('in_invoice', 'in_refund'): + self.ncf = False + if self.journal_id.purchase_type == "normal": + self.ncf_required = True + else: + self.ncf_required = False + + return super(AccountInvoice, self)._onchange_journal_id() + + @api.onchange("fiscal_position_id") + def onchange_fiscal_position_id(self): + + if self.type in ('out_invoice', 'out_refund'): + if self.fiscal_position_id.client_fiscal_type == "final": + self.journal_id = self.shop_id.final.id + elif self.fiscal_position_id.client_fiscal_type == "fiscal": + self.journal_id = self.shop_id.fiscal.id + elif self.fiscal_position_id.client_fiscal_type == "gov": + self.journal_id = self.shop_id.gov.id + elif self.fiscal_position_id.client_fiscal_type == "special": + self.journal_id = self.shop_id.special.id + else: + self.journal_id = self.shop_id.final.id + + self.shop_id = self.env["shop.ncf.config"].get_default_shop() + + elif self.type in ('in_invoice', 'in_refund'): + if self.partner_id.journal_id: + self.journal_id = self.partner_id.journal_id.id + else: + self.journal_id = self.fiscal_position_id.journal_id.id + + if self.fiscal_position_id.supplier_fiscal_type in ("01","02","03","04","05","06","07","08","09","10","11"): + self.ncf_required = True + else: + self.ncf_required = False + + def _check_ncf(self, rnc, ncf): + if ncf and rnc: + res = requests.get('http://api.marcos.do/ncf/{}/{}'.format(rnc, ncf)) + if res.status_code == 200: + return res.json() + return {} + + @api.multi + def invoice_ncf_validation(self): + for invoice in self: + if not invoice.journal_id.purchase_type in ['minor', 'informal', 'exterior'] and invoice.ncf_required == True: + + inv_exist = self.search([('partner_id','=',invoice.partner_id.id),('number','=',invoice.ncf),('state','in',('open','paid'))]) + if inv_exist: + raise exceptions.Warning(u"Este número de comprobante ya fue registrado para este proveedor!") + + if not is_ncf(invoice.ncf, invoice.type): + raise exceptions.UserError("El numero de comprobante fiscal no es valido" + "verifique de que no esta digitando un comprobante" + "de consumidor final codigo 02 o revise si lo ha " + "digitado incorrectamente") + + elif _internet_on(): + result = self._check_ncf(invoice.partner_id.vat, invoice.ncf) + if not result.get("valid", False): + raise exceptions.UserError("El numero de comprobante fiscal no es valido! " + "no paso la validacion en DGII, Verifique que el NCF y el RNC del " + "proveedor esten correctamente digitados.") + + self.signal_workflow("invoice_open") + + @api.model + def create(self, vals): + if self._context.get("type", False) in ('in_invoice', 'in_refund') and vals.get("ncf", False): + vals.update({"move_name": vals["ncf"]}) + return super(AccountInvoice, self).create(vals) + + + @api.multi + def write(self, vals): + if vals.get("ncf", False) and self._context.get("type", False) in ('in_invoice', 'in_refund'): + vals.update({"move_name": vals["ncf"]}) + return super(AccountInvoice, self).write(vals) diff --git a/models/account_invoice_state.py b/models/account_invoice_state.py new file mode 100644 index 000000000..c780ec918 --- /dev/null +++ b/models/account_invoice_state.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +from openerp import models, api, _, fields +from openerp.exceptions import UserError + + +class AccountInvoiceCancel(models.TransientModel): + """ + This wizard will cancel the all the selected invoices. + If in the journal, the option allow cancelling entry is not selected then it will give warning message. + """ + + _inherit = "account.invoice.cancel" + _description = "Cancel the Selected Invoices" + + anulation_type = fields.Selection([ + ("01", u"01 - DETERIORO DE FACTURA PRE-IMPRESA"), + ("02", u"02 - ERRORES DE IMPRESIÓN (FACTURA PRE-IMPRESA)"), + ("03", u"03 - IMPRESIÓN DEFECTUOSA"), + ("04", u"04 - DUPLICIDAD DE FACTURA"), + ("05", u"05 - CORRECCIÓN DE LA INFORMACIÓN"), + ("06", u"06 - CAMBIO DE PRODUCTOS"), + ("07", u"07 - DEVOLUCIÓN DE PRODUCTOS"), + ("08", u"08 - OMISIÓN DE PRODUCTOS"), + ("09", u"09 - ERRORES EN SECUENCIA DE NCF") + ], string=u"Tipo de anulación", required=True) + + @api.multi + def invoice_cancel(self): + context = dict(self._context or {}) + active_ids = context.get('active_ids', []) or [] + + for record in self.env['account.invoice'].browse(active_ids): + if record.state in ('cancel', 'paid'): + raise UserError(_("Selected invoice(s) cannot be cancelled as they are already in 'Cancelled' or 'Done' state.")) + record.write({"anulation_type": self.anulation_type}) + record.signal_workflow('invoice_cancel') + return {'type': 'ir.actions.act_window_close'} \ No newline at end of file diff --git a/models/dgii_cancel.py b/models/dgii_cancel.py new file mode 100644 index 000000000..86eda81ee --- /dev/null +++ b/models/dgii_cancel.py @@ -0,0 +1,133 @@ +# -*- coding: utf-8 -*- + +from openerp import models, fields, api, exceptions +import calendar +import base64 +from tools import is_identification, is_ncf +import time + +class DgiiCancelReport(models.Model): + _name = "dgii.cancel.report" + + + def get_default_period(self): + self.year = int(time.strftime("%Y")) + + + company_id = fields.Many2one('res.company', string='Company', required=True, + default=lambda self: self.env['res.company']._company_default_get('dgii.cancel.report')) + name = fields.Char() + year = fields.Integer(u"Año", size=4, default=lambda s: int(time.strftime("%Y"))) + month = fields.Integer("Mes", size=2, default=lambda s: int(time.strftime("%m"))) + CANTIDAD_REGISTRO = fields.Integer("Cantidad de registros") + report_lines = fields.One2many("dgii.cancel.report.line", "cancel_report_id") + txt = fields.Binary(u"Reporte TXT", readonly=True) + txt_name = fields.Char(readonly=True) + state = fields.Selection([('draft','Nuevo'),('done','Generado')], default="draft") + + + @api.model + def create(self, vals): + vals.update({"name": "{}/{}".format(vals["month"],vals["year"])}) + self = super(DgiiCancelReport, self).create(vals) + self.create_report() + return self + + def get_date_range(self): + if self.month > 12 or self.month < 1: + self.month = False + raise exceptions.ValidationError("El mes es invalido!") + last_day = calendar.monthrange(self.year, self.month)[1] + return ("{}-{}-{}".format(str(self.year), str(self.month).zfill(2), "01"), + "{}-{}-{}".format(str(self.year), str(self.month).zfill(2), str(last_day).zfill(2))) + + def create_report_lines(self, invoices): + if self._context.get("recreate", False): + self.report_lines.unlink() + self.txt = False + lines = [] + line_number = 1 + for inv in invoices: + line = [] + + LINE = line_number + + NUMERO_COMPROBANTE_FISCAL = inv.move_name + + FECHA_COMPROBANTE = inv.date_invoice + + TIPO_ANULACION = inv.anulation_type + + lines.append((0,False,{"LINE":LINE, + "NUMERO_COMPROBANTE_FISCAL":NUMERO_COMPROBANTE_FISCAL, + "FECHA_COMPROBANTE":FECHA_COMPROBANTE, + "TIPO_ANULACION": TIPO_ANULACION + })) + + line_number += 1 + + CANTIDAD_REGISTRO = len(lines) + + res = self.write({"report_lines": lines, + "CANTIDAD_REGISTRO": CANTIDAD_REGISTRO, + "state": "done"}) + + return res + + def generate_txt(self): + + if not self.company_id.vat or not is_identification(self.company_id.vat): + raise exceptions.ValidationError("Debe de configurar el RNC de su empresa!") + + path = '/tmp/608{}.txt'.format(self.company_id.vat) + file = open(path,'w') + lines = [] + + header = "608" + header += self.company_id.vat.zfill(11) + header += str(self.year) + header += str(self.month).zfill(2) + lines.append(header) + + for line in self.report_lines: + ln = "" + ln += line.NUMERO_COMPROBANTE_FISCAL + ln += line.FECHA_COMPROBANTE.replace("-","") + ln += "{}".format(line.TIPO_ANULACION).zfill(12) + lines.append(ln) + + for line in lines: + file.write(line+"\n") + + file.close() + file = open(path,'rb') + report = base64.b64encode(file.read()) + report_name = 'DGII_608_{}_{}{}.TXT'.format(self.company_id.vat, str(self.year), str(self.month).zfill(2)) + self.write({'txt': report, 'txt_name': report_name}) + + @api.multi + def create_report(self): + start_date, end_date = self.get_date_range() + invoices = self.env["account.invoice"].search([('date_invoice','>=',start_date), + ('date_invoice','<=',end_date), + ('state','=','cancel'), + ('type','in',('out_invoice','out_refund'))]) + + self.create_report_lines(invoices) + self.generate_txt() + return True + + +class DgiiCancelReportline(models.Model): + _name = "dgii.cancel.report.line" + + + cancel_report_id = fields.Many2one("dgii.cancel.report") + LINE = fields.Integer("Linea") + NUMERO_COMPROBANTE_FISCAL = fields.Char("NCF", size=19) + FECHA_COMPROBANTE = fields.Date("Fecha") + TIPO_ANULACION = fields.Char(u"Tipo de anulación", size=2) + + + + diff --git a/models/dgii_exterior.py b/models/dgii_exterior.py new file mode 100644 index 000000000..103c0e53e --- /dev/null +++ b/models/dgii_exterior.py @@ -0,0 +1,173 @@ +# -*- coding: utf-8 -*- + +from openerp import models, fields, api, exceptions +import calendar +import base64 +from tools import is_identification, is_ncf +import time + + +class DgiiExteriorReport(models.Model): + _name = "dgii.exterior.report" + + company_id = fields.Many2one('res.company', string='Company', required=True, + default=lambda self: self.env['res.company']._company_default_get( + 'dgii.exterior.report')) + name = fields.Char() + year = fields.Integer(u"Año", size=4, default=lambda s: int(time.strftime("%Y"))) + month = fields.Integer("Mes", size=2, default=lambda s: int(time.strftime("%m"))) + CANTIDAD_REGISTRO = fields.Integer("Cantidad de registros") + TOTAL_MONTO_FACTURADO = fields.Float("TOTAL FACTURADO") + report_lines = fields.One2many("dgii.exterior.report.line", "exterior_report_id") + txt = fields.Binary(u"Reporte TXT", readonly=True) + txt_name = fields.Char(readonly=True) + state = fields.Selection([('draft', 'Nuevo'), ('done', 'Generado')], default="draft") + + @api.model + def create(self, vals): + vals.update({"name": "{}/{}".format(vals["month"], vals["year"])}) + self = super(DgiiExteriorReport, self).create(vals) + self.create_report() + return self + + def get_date_range(self): + if self.month > 12 or self.month < 1: + self.month = False + raise exceptions.ValidationError("El mes es invalido!") + last_day = calendar.monthrange(self.year, self.month)[1] + return ("{}-{}-{}".format(str(self.year), str(self.month).zfill(2), "01"), + "{}-{}-{}".format(str(self.year), str(self.month).zfill(2), str(last_day).zfill(2))) + + def create_report_lines(self, invoices): + if self._context.get("recreate", False): + self.report_lines.unlink() + self.txt = False + lines = [] + line_number = 1 + for inv in invoices: + line = [] + + LINE = line_number + + RAZON_SOCIAL = inv.partner_id.name + + TIPO_BIENES_SERVICIOS_COMPRADOS = inv.fiscal_position_id.supplier_fiscal_type + + if not TIPO_BIENES_SERVICIOS_COMPRADOS: + raise exceptions.ValidationError( + u"Debe de definir el tipo de gasto para la posición fiscal {}!".format(inv.fiscal_position_id.name)) + + FECHA_FACTURA = inv.date_invoice + FECHA_RETENCION_ISR = False + + MONTO_FACTURADO = 0.00 + + for line in inv.invoice_line_ids: + account_ids = [l.account_id.id for l in line] + move_lines = self.env["account.move.line"].search( + [('move_id', '=', inv.move_id.id), ('account_id', 'in', account_ids)]) + MONTO_FACTURADO += sum([l.debit for l in move_lines]) - sum([l.credit for l in move_lines]) + if inv.type == "in_refund": + MONTO_FACTURADO = MONTO_FACTURADO * -1 + + MONTO_FACTURADO = MONTO_FACTURADO + + ISR_RETENCION = 0 + + for tax in inv.tax_line_ids: + if tax.tax_id.purchase_tax_type == "itbis": + raise exceptions.UserError("Impuesto invalido para el tipo de comprobante!") + elif tax.tax_id.purchase_tax_type == "ritbis": + raise exceptions.UserError("Impuesto invalido para el tipo de comprobante!") + else: + account_ids = [t.account_id.id for t in tax] + move_lines = self.env["account.move.line"].search( + [('move_id', '=', inv.move_id.id), ('account_id', 'in', account_ids)]) + ISR_RETENCION += sum([l.debit for l in move_lines]) - sum([l.credit for l in move_lines]) * -1 + + lines.append((0, False, {"LINE": LINE, + "RAZON_SOCIAL": RAZON_SOCIAL, + "TIPO_BIENES_SERVICIOS_COMPRADOS": TIPO_BIENES_SERVICIOS_COMPRADOS, + "FECHA_FACTURA": FECHA_FACTURA, + "FECHA_RETENCION_ISR": FECHA_RETENCION_ISR, + "ISR_RETENCION": ISR_RETENCION, + "MONTO_FACTURADO": MONTO_FACTURADO + })) + + line_number += 1 + + CANTIDAD_REGISTRO = len(lines) + TOTAL_MONTO_FACTURADO = sum([line[2]["MONTO_FACTURADO"] for line in lines]) + + + res = self.write({"report_lines": lines, + "CANTIDAD_REGISTRO": CANTIDAD_REGISTRO, + "TOTAL_MONTO_FACTURADO": TOTAL_MONTO_FACTURADO, + "state": "done"}) + return res + + + def generate_txt(self): + if not self.company_id.vat or not is_identification(self.company_id.vat): + raise exceptions.ValidationError("Debe de configurar el RNC de su empresa!") + + path = '/tmp/609{}.txt'.format(self.company_id.vat) + file = open(path, 'w') + lines = [] + + header = "609" + header += self.company_id.vat.zfill(11) + header += str(self.year) + header += str(self.month).zfill(2) + header += "{:.2f}".format(self.TOTAL_MONTO_FACTURADO).zfill(16) + lines.append(header) + + for line in self.report_lines: + ln = "" + ln += line.RAZON_SOCIAL.rjust(30) + ln += line.TIPO_BIENES_SERVICIOS_COMPRADOS + ln += line.FECHA_FACTURA.replace("-", "") + ln += line.FECHA_RETENCION_ISR if line.FECHA_RETENCION_ISR else "".rjust(8) + ln += "{:.2f}".format(line.ISR_RETENCION).zfill(12) + ln += "{:.2f}".format(line.MONTO_FACTURADO).zfill(12) + lines.append(ln) + + + line_count = 1 + for l in lines: + print "line count {} {}".format(line_count, l) + line_count += 1 + file.write(l + "\n") + + file.close() + file = open(path, 'rb') + report = base64.b64encode(file.read()) + report_name = 'DGII_609_{}_{}{}.TXT'.format(self.company_id.vat, str(self.year), str(self.month).zfill(2)) + self.write({'txt': report, 'txt_name': report_name}) + + + @api.multi + def create_report(self): + start_date, end_date = self.get_date_range() + exterior_journal_ids = [rec.id for rec in self.env["account.journal"].search([('purchase_type', '=', 'exterior')])] + invoices = self.env["account.invoice"].search( + [('date_invoice', '>=', start_date), ('date_invoice', '<=', end_date), ('state', 'in', ('open', 'paid')), + ('type', '=', 'in_invoice'), ('journal_id', 'in', exterior_journal_ids)]) + self.create_report_lines(invoices) + self.generate_txt() + return True + + +class DgiiExteriorReportline(models.Model): + _name = "dgii.exterior.report.line" + + exterior_report_id = fields.Many2one("dgii.exterior.report") + LINE = fields.Integer("Linea") + RNC_CEDULA = fields.Char(u"RNC", size=11) + RAZON_SOCIAL = fields.Char("Razon Social") + TIPO_IDENTIFICACION = fields.Char("Tipo ID", size=1) + TIPO_BIENES_SERVICIOS_COMPRADOS = fields.Char("Tipo", size=2) + FECHA_FACTURA = fields.Date("Fecha") + FECHA_RETENCION_ISR = fields.Date("Fecha retencion ISR") + ISR_RETENCION = fields.Float("ISR Retenido") + MONTO_FACTURADO = fields.Float("Monto Facturado") diff --git a/models/dgii_purchase.py b/models/dgii_purchase.py new file mode 100644 index 000000000..07457915b --- /dev/null +++ b/models/dgii_purchase.py @@ -0,0 +1,224 @@ +# -*- coding: utf-8 -*- + +from openerp import models, fields, api, exceptions +import calendar +import base64 +from tools import is_identification, is_ncf +import time + + +class DgiiPurchaseReport(models.Model): + _name = "dgii.purchase.report" + + + company_id = fields.Many2one('res.company', string='Company', required=True, + default=lambda self: self.env['res.company']._company_default_get('dgii.purchase.report')) + name = fields.Char() + year = fields.Integer(u"Año", size=4, default=lambda s: int(time.strftime("%Y"))) + month = fields.Integer("Mes", size=2, default=lambda s: int(time.strftime("%m"))) + CANTIDAD_REGISTRO = fields.Integer("Cantidad de registros") + ITBIS_RETENIDO = fields.Float("TOTAL ITBIS RETENIDO") + ITBIS_TOTAL = fields.Float("TOTAL ITBIS PAGADO") + TOTAL_MONTO_FACTURADO = fields.Float("TOTAL FACTURADO") + RETENCION_RENTA = fields.Float("TOTAL RETENCION RENTA") + report_lines = fields.One2many("dgii.purchase.report.line", "purchase_report_id") + txt = fields.Binary(u"Reporte TXT", readonly=True) + txt_name = fields.Char(readonly=True) + state = fields.Selection([('draft','Nuevo'),('done','Generado')], default="draft") + + @api.model + def create(self, vals): + vals.update({"name": "{}/{}".format(vals["month"],vals["year"])}) + self = super(DgiiPurchaseReport, self).create(vals) + self.create_report() + return self + + def get_date_range(self): + if self.month > 12 or self.month < 1: + self.month = False + raise exceptions.ValidationError("El mes es invalido!") + last_day = calendar.monthrange(self.year, self.month)[1] + return ("{}-{}-{}".format(str(self.year), str(self.month).zfill(2), "01"), + "{}-{}-{}".format(str(self.year), str(self.month).zfill(2), str(last_day).zfill(2))) + + def create_report_lines(self, invoices): + if self._context.get("recreate", False): + self.report_lines.unlink() + self.txt = False + lines = [] + line_number = 1 + for inv in invoices: + line = [] + + LINE = line_number + + if not is_identification(inv.partner_id.vat): + raise exceptions.ValidationError(u"El número de RNC/Cédula para el proveedor {} no es valido!".format(inv.partner_id.name)) + + RNC_CEDULA = inv.partner_id.vat + TIPO_IDENTIFICACION = "1" if len(RNC_CEDULA.strip()) == 9 else "2" + + TIPO_BIENES_SERVICIOS_COMPRADOS = inv.fiscal_position_id.supplier_fiscal_type + + if not TIPO_BIENES_SERVICIOS_COMPRADOS: + raise exceptions.ValidationError(u"Debe de definir el tipo de gasto para la posición fiscal {}!".format(inv.fiscal_position_id.name)) + + if not is_ncf(inv.number, inv.type): + raise exceptions.ValidationError(u"El número de NCF {} no es valido!".format(inv.number)) + + + NUMERO_COMPROBANTE_MODIFICADO = "".rjust(19) + if inv.type == "in_invoice": + NUMERO_COMPROBANTE_FISCAL = inv.number + elif inv.type == "in_refund": + NUMERO_COMPROBANTE_FISCAL = inv.number + NUMERO_COMPROBANTE_MODIFICADO = inv.origin + + FECHA_COMPROBANTE = inv.date_invoice + if inv.payment_move_line_ids: + FECHA_PAGO = max(inv.payment_move_line_ids).date + else: + FECHA_PAGO = False + + MONTO_FACTURADO = 0.00 + for line in inv.invoice_line_ids: + account_ids = [l.account_id.id for l in line] + move_lines = self.env["account.move.line"].search([('move_id','=',inv.move_id.id),('account_id','in',account_ids)]) + MONTO_FACTURADO += sum([l.debit for l in move_lines])-sum([l.credit for l in move_lines]) + if inv.type == "in_refund": + MONTO_FACTURADO = MONTO_FACTURADO * -1 + + + MONTO_FACTURADO = MONTO_FACTURADO + + ITBIS_FACTURADO = 0 + ITBIS_RETENIDO = 0 + RETENCION_RENTA = 0 + + for tax in inv.tax_line_ids: + if tax.tax_id.purchase_tax_type == "itbis": + account_ids = [t.account_id.id for t in tax] + move_lines = self.env["account.move.line"].search([('move_id','=',inv.move_id.id),('account_id','in',account_ids)]) + ITBIS_FACTURADO += sum([l.debit for l in move_lines])-sum([l.credit for l in move_lines]) + if inv.type == "in_refund": + ITBIS_FACTURADO = ITBIS_FACTURADO * -1 + elif tax.tax_id.purchase_tax_type == "ritbis": + account_ids = [t.account_id.id for t in tax] + move_lines = self.env["account.move.line"].search([('move_id','=',inv.move_id.id),('account_id','in',account_ids)]) + ITBIS_RETENIDO += sum([l.debit for l in move_lines])-sum([l.credit for l in move_lines]) + if inv.type == "in_refund": + ITBIS_RETENIDO = ITBIS_RETENIDO * -1 + else: + account_ids = [t.account_id.id for t in tax] + move_lines = self.env["account.move.line"].search([('move_id','=',inv.move_id.id),('account_id','in',account_ids)]) + RETENCION_RENTA += sum([l.debit for l in move_lines])-sum([l.credit for l in move_lines]) + if inv.type == "in_refund": + RETENCION_RENTA = RETENCION_RENTA * -1 + + + lines.append((0,False,{"LINE":LINE, + "RNC_CEDULA":RNC_CEDULA, + "TIPO_IDENTIFICACION":TIPO_IDENTIFICACION, + "TIPO_BIENES_SERVICIOS_COMPRADOS":TIPO_BIENES_SERVICIOS_COMPRADOS, + "NUMERO_COMPROBANTE_FISCAL":NUMERO_COMPROBANTE_FISCAL, + "NUMERO_COMPROBANTE_MODIFICADO":NUMERO_COMPROBANTE_MODIFICADO, + "FECHA_COMPROBANTE":FECHA_COMPROBANTE, + "FECHA_PAGO":FECHA_PAGO, + "ITBIS_FACTURADO":ITBIS_FACTURADO, + "ITBIS_RETENIDO":ITBIS_RETENIDO, + "MONTO_FACTURADO":MONTO_FACTURADO, + "RETENCION_RENTA":RETENCION_RENTA + })) + + + line_number += 1 + + CANTIDAD_REGISTRO = len(lines) + ITBIS_RETENIDO = sum([line[2]["ITBIS_RETENIDO"] for line in lines]) + ITBIS_TOTAL = sum([line[2]["ITBIS_FACTURADO"] for line in lines]) + TOTAL_MONTO_FACTURADO = sum([line[2]["MONTO_FACTURADO"] for line in lines]) + RETENCION_RENTA = sum([line[2]["RETENCION_RENTA"] for line in lines]) + + res = self.write({"report_lines": lines, + "CANTIDAD_REGISTRO": CANTIDAD_REGISTRO, + "ITBIS_RETENIDO": ITBIS_RETENIDO, + "ITBIS_TOTAL": ITBIS_TOTAL, + "TOTAL_MONTO_FACTURADO": TOTAL_MONTO_FACTURADO, + "RETENCION_RENTA": RETENCION_RENTA, + "state": "done"}) + + def generate_txt(self): + + if not self.company_id.vat or not is_identification(self.company_id.vat): + raise exceptions.ValidationError("Debe de configurar el RNC de su empresa!") + + path = '/tmp/606{}.txt'.format(self.company_id.vat) + file = open(path,'w') + lines = [] + + header = "606" + header += self.company_id.vat.zfill(11) + header += str(self.year) + header += str(self.month).zfill(2) + header += "{:.2f}".format(self.TOTAL_MONTO_FACTURADO).zfill(16) + header += "{:.2f}".format(self.RETENCION_RENTA).zfill(12) + lines.append(header) + + for line in self.report_lines: + ln = "" + ln += line.RNC_CEDULA.rjust(11) + ln += line.TIPO_IDENTIFICACION + ln += line.TIPO_BIENES_SERVICIOS_COMPRADOS + ln += line.NUMERO_COMPROBANTE_FISCAL + ln += line.NUMERO_COMPROBANTE_MODIFICADO + ln += line.FECHA_COMPROBANTE.replace("-","") + ln += line.FECHA_PAGO.replace("-","") if line.FECHA_PAGO else "".rjust(8) + ln += "{:.2f}".format(line.ITBIS_FACTURADO).zfill(12) + ln += "{:.2f}".format(line.ITBIS_RETENIDO).zfill(12) + ln += "{:.2f}".format(line.MONTO_FACTURADO).zfill(12) + ln += "{:.2f}".format(line.RETENCION_RENTA).zfill(12) + lines.append(ln) + + for line in lines: + file.write(line+"\n") + + file.close() + file = open(path,'rb') + report = base64.b64encode(file.read()) + report_name = 'DGII_606_{}_{}{}.TXT'.format(self.company_id.vat, str(self.year), str(self.month).zfill(2)) + self.write({'txt': report, 'txt_name': report_name}) + + @api.multi + def create_report(self): + start_date, end_date = self.get_date_range() + invoices = self.env["account.invoice"].search([('date_invoice','>=',start_date), + ('date_invoice','<=',end_date), + ('state','in',('open','paid')), + ('type','in',('in_invoice','in_refund'))]) + + self.create_report_lines(invoices) + self.generate_txt() + return True + + +class DgiiPurchaseReportline(models.Model): + _name = "dgii.purchase.report.line" + + + purchase_report_id = fields.Many2one("dgii.purchase.report") + LINE = fields.Integer("Linea") + RNC_CEDULA = fields.Char(u"RNC", size=11) + TIPO_IDENTIFICACION= fields.Char("Tipo ID", size=1) + TIPO_BIENES_SERVICIOS_COMPRADOS = fields.Char("Tipo", size=2) + NUMERO_COMPROBANTE_FISCAL = fields.Char("NCF", size=19) + NUMERO_COMPROBANTE_MODIFICADO = fields.Char("Afecta", size=19) + FECHA_COMPROBANTE = fields.Date("Fecha") + FECHA_PAGO = fields.Date("Pagado") + ITBIS_FACTURADO = fields.Float("ITBIS Facturado") + ITBIS_RETENIDO = fields.Float("ITBIS Retenido") + MONTO_FACTURADO = fields.Float("Monto Facturado") + RETENCION_RENTA = fields.Float(u"Retención Renta") + + + + diff --git a/models/dgii_sale.py b/models/dgii_sale.py new file mode 100644 index 000000000..6dcb725eb --- /dev/null +++ b/models/dgii_sale.py @@ -0,0 +1,193 @@ +# -*- coding: utf-8 -*- + +from openerp import models, fields, api, exceptions +import calendar +import base64 +from tools import is_identification, is_ncf +import time + + +class DgiiSaleReport(models.Model): + _name = "dgii.sale.report" + + def get_default_period(self): + self.year = int(time.strftime("%Y")) + + + company_id = fields.Many2one('res.company', string='Company', required=True, + default=lambda self: self.env['res.company']._company_default_get('dgii.sale.report')) + name = fields.Char() + year = fields.Integer(u"Año", size=4, default=lambda s: int(time.strftime("%Y"))) + month = fields.Integer("Mes", size=2, default=lambda s: int(time.strftime("%m"))) + CANTIDAD_REGISTRO = fields.Integer("Cantidad de registros") + ITBIS_TOTAL = fields.Float("TOTAL ITBIS PAGADO") + TOTAL_MONTO_FACTURADO = fields.Float("TOTAL FACTURADO") + report_lines = fields.One2many("dgii.sale.report.line", "sale_report_id") + txt = fields.Binary(u"Reporte TXT", readonly=True) + txt_name = fields.Char(readonly=True) + state = fields.Selection([('draft','Nuevo'),('done','Generado')], default="draft") + + + @api.model + def create(self, vals): + vals.update({"name": "{}/{}".format(vals["month"],vals["year"])}) + self = super(DgiiSaleReport, self).create(vals) + self.create_report() + return self + + def get_date_range(self): + if self.month > 12 or self.month < 1: + self.month = False + raise exceptions.ValidationError("El mes es invalido!") + last_day = calendar.monthrange(self.year, self.month)[1] + return ("{}-{}-{}".format(str(self.year), str(self.month).zfill(2), "01"), + "{}-{}-{}".format(str(self.year), str(self.month).zfill(2), str(last_day).zfill(2))) + + def create_report_lines(self, invoices): + if self._context.get("recreate", False): + self.report_lines.unlink() + self.txt = False + lines = [] + line_number = 1 + for inv in invoices: + line = [] + + LINE = line_number + + if not is_identification(inv.partner_id.vat): + raise exceptions.ValidationError(u"El número de RNC/Cédula para el proveedor {} no es valido!".format(inv.partner_id.name)) + + RNC_CEDULA = inv.partner_id.vat + TIPO_IDENTIFICACION = "1" if len(RNC_CEDULA.strip()) == 9 else "2" + + if not is_ncf(inv.number, inv.type): + raise exceptions.ValidationError(u"El número de NCF {} no es valido!".format(inv.number)) + + + NUMERO_COMPROBANTE_MODIFICADO = "".rjust(19) + if inv.type == "out_invoice": + NUMERO_COMPROBANTE_FISCAL = inv.number + elif inv.type == "out_refund": + NUMERO_COMPROBANTE_FISCAL = inv.number + NUMERO_COMPROBANTE_MODIFICADO = inv.origin + + FECHA_COMPROBANTE = inv.date_invoice + if inv.payment_move_line_ids: + FECHA_PAGO = max(inv.payment_move_line_ids).date + else: + FECHA_PAGO = False + + MONTO_FACTURADO = 0.00 + for line in inv.invoice_line_ids: + account_ids = [l.account_id.id for l in line] + move_lines = self.env["account.move.line"].search([('move_id','=',inv.move_id.id),('account_id','in',account_ids)]) + MONTO_FACTURADO += sum([l.debit for l in move_lines])-sum([l.credit for l in move_lines]) + if inv.type == "in_refund": + MONTO_FACTURADO = MONTO_FACTURADO * -1 + + + MONTO_FACTURADO = MONTO_FACTURADO + + ITBIS_FACTURADO = 0 + + for tax in inv.tax_line_ids: + account_ids = [t.account_id.id for t in tax] + move_lines = self.env["account.move.line"].search([('move_id','=',inv.move_id.id),('account_id','in',account_ids)]) + ITBIS_FACTURADO += sum([l.debit for l in move_lines])-sum([l.credit for l in move_lines]) + if inv.type == "in_refund": + ITBIS_FACTURADO = ITBIS_FACTURADO * -1 + + lines.append((0,False,{"LINE":LINE, + "RNC_CEDULA":RNC_CEDULA, + "TIPO_IDENTIFICACION":TIPO_IDENTIFICACION, + "NUMERO_COMPROBANTE_FISCAL":NUMERO_COMPROBANTE_FISCAL, + "NUMERO_COMPROBANTE_MODIFICADO":NUMERO_COMPROBANTE_MODIFICADO, + "FECHA_COMPROBANTE":FECHA_COMPROBANTE, + "FECHA_PAGO":FECHA_PAGO, + "ITBIS_FACTURADO":ITBIS_FACTURADO, + "MONTO_FACTURADO":MONTO_FACTURADO + })) + + + line_number += 1 + + CANTIDAD_REGISTRO = len(lines) + ITBIS_TOTAL = sum([line[2]["ITBIS_FACTURADO"] for line in lines]) + TOTAL_MONTO_FACTURADO = sum([line[2]["MONTO_FACTURADO"] for line in lines]) + + res = self.write({"report_lines": lines, + "CANTIDAD_REGISTRO": CANTIDAD_REGISTRO, + "ITBIS_TOTAL": ITBIS_TOTAL, + "TOTAL_MONTO_FACTURADO": TOTAL_MONTO_FACTURADO, + "state": "done"}) + + return res + + def generate_txt(self): + + if not self.company_id.vat or not is_identification(self.company_id.vat): + raise exceptions.ValidationError("Debe de configurar el RNC de su empresa!") + + path = '/tmp/607{}.txt'.format(self.company_id.vat) + file = open(path,'w') + lines = [] + + header = "607" + header += self.company_id.vat.zfill(11) + header += str(self.year) + header += str(self.month).zfill(2) + header += "{:.2f}".format(self.TOTAL_MONTO_FACTURADO).zfill(16) + lines.append(header) + + for line in self.report_lines: + ln = "" + ln += line.RNC_CEDULA.rjust(11) + ln += line.TIPO_IDENTIFICACION + ln += line.NUMERO_COMPROBANTE_FISCAL + ln += line.NUMERO_COMPROBANTE_MODIFICADO + ln += line.FECHA_COMPROBANTE.replace("-","") + ln += line.FECHA_PAGO.replace("-","") if line.FECHA_PAGO else "".rjust(8) + ln += "{:.2f}".format(line.ITBIS_FACTURADO).zfill(12) + ln += "{:.2f}".format(line.MONTO_FACTURADO).zfill(12) + lines.append(ln) + + for line in lines: + file.write(line+"\n") + + file.close() + file = open(path,'rb') + report = base64.b64encode(file.read()) + report_name = 'DGII_607_{}_{}{}.TXT'.format(self.company_id.vat, str(self.year), str(self.month).zfill(2)) + self.write({'txt': report, 'txt_name': report_name}) + + @api.multi + def create_report(self): + start_date, end_date = self.get_date_range() + invoices = self.env["account.invoice"].search([('date_invoice','>=',start_date), + ('date_invoice','<=',end_date), + ('state','in',('open','paid')), + ('type','in',('out_invoice','out_refund'))]) + import pdb;pdb.set_trace() + self.create_report_lines(invoices) + self.generate_txt() + return True + + +class DgiiSaleReportline(models.Model): + _name = "dgii.sale.report.line" + + + sale_report_id = fields.Many2one("dgii.sale.report") + LINE = fields.Integer("Linea") + RNC_CEDULA = fields.Char(u"RNC", size=11) + TIPO_IDENTIFICACION= fields.Char("Tipo ID", size=1) + NUMERO_COMPROBANTE_FISCAL = fields.Char("NCF", size=19) + NUMERO_COMPROBANTE_MODIFICADO = fields.Char("Afecta", size=19) + FECHA_COMPROBANTE = fields.Date("Fecha") + FECHA_PAGO = fields.Date("Pagado") + ITBIS_FACTURADO = fields.Float("ITBIS Facturado") + MONTO_FACTURADO = fields.Float("Monto Facturado") + + + + diff --git a/models/res_partner.py b/models/res_partner.py new file mode 100644 index 000000000..ebaff3180 --- /dev/null +++ b/models/res_partner.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- + +from openerp import models, fields + + +class ResPartner(models.Model): + _inherit = "res.partner" + + journal_id = fields.Many2one("account.journal", "Diario de compra") \ No newline at end of file diff --git a/models/shop.py b/models/shop.py new file mode 100644 index 000000000..8c1baceed --- /dev/null +++ b/models/shop.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- + + +from openerp import models, fields, api, exceptions + + +class ShopJournalConfig(models.Model): + _name = "shop.ncf.config" + + + name = fields.Char("Nombre", size=40, required=True) + final = fields.Many2one("account.journal", "Consumidor final", required=False, domain="[('type','=','sale')]") + fiscal = fields.Many2one("account.journal", u"Para crédito fiscal", required=False, domain="[('type','=','sale')]") + special = fields.Many2one("account.journal", u"Regímes especiales", required=False, domain="[('type','=','sale')]") + gov = fields.Many2one("account.journal", "Gubernalmentales", required=False, domain="[('type','=','sale')]") + user_ids = fields.Many2many("res.users", string="Usuarios que pueden usar esta sucursal") + + + _sql_constraints = [ + ('shop_ncf_config_name_uniq', 'unique(name)', 'El nombre de la sucursal debe de ser unico!'), + ] + + def get_user_shop_domain(self): + user_shops = self.search([('user_ids','=',self._uid)]) + return [('id','=',[r.id for r in user_shops])] + + @api.v8 + @api.multi + def get_default_shop(self): + try: + user_shops = self.search([('user_ids','=',self._uid)]) + if user_shops: + if len(user_shops) > 1: + return user_shops[0].id + else: + return user_shops.id + else: + raise exceptions.Warning(u"Se debe realizar la configuración de los comprobantes fiscales antes de realizar una factura!") + except: + return False \ No newline at end of file diff --git a/models/tools.py b/models/tools.py new file mode 100644 index 000000000..ae58cc9b2 --- /dev/null +++ b/models/tools.py @@ -0,0 +1,558 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Copyright (C) 2013-2015 Marcos Organizador de Negocios SRL http://marcos.do +# Write by Eneldo Serrata (eneldo@marcos.do) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +import csv +import cPickle +import re +import urllib2 + +__author__ = 'eneldoserrata' + +excepcionesCedulas = ['00000000018', '11111111123', '00100759932', '00105606543', '00114272360', '00200123640', + '00200409772', '00800106971', '01200004166', '01400074875', '01400000282', '03103749672', + '03200066940', '03800032522', '03900192284', '04900026260', '05900072869', '07700009346', + '00114532330', '03121982479', '40200700675', '40200639953', '00121581750', '00119161853', + '22321581834', '00121581800', '09421581768', '22721581818', '90001200901', '00301200901', + '40200452735', '40200401324', '10621581792', '00208430205', '00101118022', '00167311001', + '00102025201', '02755972001', '01038813907', '01810035037', '00161884001', '00102630192', + '00000021249', '00144435001', '00100350928', '00100523399', '00109402756', '00101659661', + '00539342005', '00104662561', '08016809001', '05500012039', '00104486903', '00103754365', + '01200014133', '10983439110', '08498619001', '00104862525', '00100729795', '00644236001', + '01650257001', '00170009162', '00651322001', '00297018001', '00100288929', '00190002567', + '01094560111', '01300020331', '00109785951', '00110047715', '05400067703', '00100061945', + '00100622461', '02831146001', '10462157001', '00100728113', '00108497822', '00481106001', + '00100181057', '10491297001', '00300244009', '00170115579', '02038569001', '00100238382', + '03852380001', '00100322649', '00107045499', '00100384523', '00130610001', '06486186001', + '00101621981', '00201023001', '00520207699', '00300636564', '00000140874', '05700071202', + '03100673050', '00189405093', '00105328185', '10061805811', '00117582001', '00103443802', + '00100756082', '00100239662', '04700027064', '04700061076', '05500023407', '05500017761', + '05400049237', '05400057300', '05600038964', '05400021759', '00100415853', '05500032681', + '05500024190', '06400011981', '05500024135', '06400007916', '05500014375', '05500008806', + '05500021118', '05600051191', '00848583056', '00741721056', '04801245892', '04700004024', + '00163709018', '05600267737', '00207327056', '00731054054', '00524571001', '00574599001', + '00971815056', '06800008448', '04900011690', '03111670001', '00134588056', '04800019561', + '05400040523', '05400048248', '05600038251', '00222017001', '06100011935', '06100007818', + '00129737056', '00540077717', '00475916056', '00720758056', '02300062066', '02700029905', + '02600094954', '11700000658', '03100109611', '04400002002', '03400157849', '03900069856', + '00100524531', '00686904003', '00196714003', '00435518003', '00189213001', '06100009131', + '02300085158', '02300047220', '00100593378', '00100083860', '00648496171', '00481595003', + '00599408003', '00493593003', '00162906003', '00208832003', '00166533003', '00181880003', + '00241997013', '00299724003', '00174729003', '01000005580', '00400012957', '00100709215', + '08900001310', '05400053627', '05400055770', '08800003986', '02300031758', '01154421047', + '00300013835', '00300011700', '01300001142', '00147485003', '00305535206', '05400054156', + '06100016486', '00100172940', '04800046910', '00101527366', '00270764013', '00184129003', + '05400033166', '05400049834', '05400062459', '09700003030', '05300013029', '05400037495', + '05400028496', '05400059956', '05400072273', '02300052220', '00356533003', '00163540003', + '00376023023', '00362684023', '00633126023', '00278005023', '00235482001', '00142864013', + '00131257003', '00236245013', '00757398001', '00146965001', '00516077003', '00425759001', + '00857630012', '06843739551', '02300023225', '00298109001', '00274652001', '00300017875', + '00300025568', '01300005424', '00103266558', '00174940001', '00289931003', '00291549003', + '02800021761', '02800029588', '01000268998', '02600036132', '00200040516', '01100014261', + '02800000129', '01200033420', '02800025877', '00300020806', '00200021994', '00200063601', + '07600000691', '09300006239', '00200028716', '04900028443', '00163549012', '01200008613', + '01200011252', '01100620962', '00100255349', '00108796883', '03102828522', '00000719400', + '00004110056', '00000065377', '00000292212', '00000078587', '00000126295', '00000111941', + '12019831001', '00171404771', '03000411295', '00000564933', '00000035692', '00143072001', + '03102936385', '00000155482', '00000236621', '00400001552', '04941042001', '00300169535', + '00102577448', '03600127038', '00100174666', '00100378440', '00104785104', '00101961125', + '05600063115', '00110071113', '00100000169', '04902549001', '00155144906', '06337850001', + '02300054193', '00100016495', '00101821735', '00544657001', '03807240010', '08952698001', + '00345425001', '06100013662', '08900005064', '05400058964', '05400022042', '05400055485', + '05400016031', '05400034790', '05400038776', '05400076481', '05400060743', '05400047674', + '00246160013', '00116256005', '00261011013', '01600026316', '00103983004', '05600037761', + '00291431001', '00100530588', '01600009531', '05500022399', '05500003079', '05500006796', + '05500027749', '06400014372', '00352861001', '00100053841', '00218507031', '02300037618', + '04600198229', '00000058035', '04700074827', '04700070460', '04700020933', '07800000968', + '00300019575', '00100126468', '00300001538'] + +excepcionesRNC = ['501478442', '501463232', '501479252', '501832602', '501477322', '501479872', '501480692', + '525001772', '531028132', '518114042', '501473602', '501859012', '502618972', '505066242', + '501348182', '531105692', '512043572', '512043742', '505055992', '502899432', '502598912', + '515123442', '505620452', '531002192', '506124652', '501482172', '531136962', '501483322', + '531025702', '502604432', '501491902', '502895232', '501481362', '523000622', '531047242', + '502610122', '501448152', '504072002', '501473122', '501141812', '522007802', '531040892', + '501374752', '504073742', '502895062', '501751432', '523020232', '501457682', '501096132', + '506507802', '523018742', '501374582', '512924072', '501985972', '523007082', '519513152', + '511105842', '531017912', '502617852', '505055852', '502855842', '501495592', '501484582', + '501134042', '502893582', '511097092', '501265082', '531011752', '531025672', '504685292', + '531007062', '531020832', '531094372', '531023912', '523001092', '503999472', '512043602', + '501477012', '531011892', '504070832', '531013992', '531048052', '516018412', '506510722', + '501448462', '502866232', '502604882', '502608012', '503970652', '502615922', '501937382', + '502602162', '531019362', '501038272', '501038442', '531121272', '503998182', '531057132', + '531113792', '504686132', '501000992', '510303072', '501078622', '504686612', '531040922', + '524003012', '501987762', '524001362', '501970282', '531021022', '501618082', '531028612', + '501200762', '501265422', '523010652', '501057242', '531127912', '531043522', '524008502', + '505621122', '524006712', '531054222', '501485252', '503518512', '531085012', '501407472', + '502895542', '502614632', '502858132', '501080392', '511100352', '531019052', '531072182', + '531046912', '523023452', '522003262', '524000412', '502610602', '501223932', '501009582', + '531050502', '531105862', '523000312', '501847502', '523018572', '531072212', '501925082', + '501042032', '501088172', '507032912', '501203532', '502611102', '501260412', '501240322', + '501048472', '501098402', '504684822', '502610572', '505623702', '531046602', '501008292', + '501249532', '531044812', '501195742', '505625802', '520001442', '511104242', '502881622', + '501207392', '501232532', '501485082', '502610912', '501060812', '523018602', '531119162', + '501050302', '523025722', '531014972', '531041562', '503996082', '501296832', '501432892', + '501066462', '524006682', '523021972', '501280472', '501215492', '501219692', '501005412', + '501154582', '501240802', '502610882', '531026202', '531026172', '501325522', '502610742', + '501275452', '507033072', '502870272', '501322442', '501321292', '501934782', '503517702', + '501334092', '501177272', '501177582', '523000592', '531029112', '501266372', '501266992', + '531029732', '501287612', '501240292', '531028892', '501269932', '501176772', '502589522', + '501432752', '505054872', '531030722', '531094402', '503996872', '501164022', '501164502', + '505055712', '501902872', '512046172', '501334742', '531053072', '501238832', '501522972', + '531034892', '505066382', '531036992', '501336982', '501490582', '502611412', '502611862', + '501555072', '505067222', '531029872', '531031842', '531031672', '501482032', '531032652', + '501400982', '531028922', '501527192', '531029562', '531122562', '501581782', '505066412', + '507031762', '531030862', '501343962', '501843272', '505067192', '531028752', '531045592', + '531030102', '502599412', '501617582', '501617612', '502611552', '502612532', '516019222', + '501552812', '501522832', '531033152', '502611382', '531033292', '531036542', '502611692', + '524008022', '505028812', '531031982', '505057332', '502611242', '501624112', '503996112', + '531034272', '502612672', '502592612', '501655042', '501430792', '501619372', '501619682', + '501749322', '523001602', '531033802', '501626832', '501408622', '501522212', '531039592', + '531032172', '502613202', '503975042', '531048702', '511099532', '502612842', '502613172', + '512920972', '501539492', '509532902', '531030072', '531032512', '511098692', '501521542', + '501521682', '501451692', '501525912', '501439862', '502612192', '501551662', '501545522', + '502888082', '501615202', '501690832', '501408282', '512046032', '502612362', '501532102', + '505612832', '502613342', '501590862', '531032202', '502611722', '501759212', '501742042', + '501470042', '501650342', '501685782', '504000842', '501552332', '531032792', '501707492', + '502893612', '511100042', '531034442', '531035562', '512046202', '505067052', '501557032', + '531032822', '531037522', '501555692', '501641742', '501584552', '514033432', '501705562', + '501540202', '531035732', '505613332', '502612222', '512921162', '505056972', '501851992', + '501609482', '501704612', '502018932', '501661182', '511106482', '501562192', '501587772', + '531035252', '512921332', '502888112', '501704272', '501729682', '503996422', '505062972', + '501600612', '520000802', '501884912', '501553762', '502861192', '501705392', '501684352', + '501648542', '502885652', '501593632', '501659102', '501638342', '501903402', '501904492', + '501904522', '502612702', '531037212', '502592582', '501685022', '501699902', '501573402', + '531032482', '502897952', '501895272', '502569912', '501906142', '505622382', '501828982', + '502507542', '501712852', '501914072', '501897402', '502571062', '503510112', '531042232', + '502861362', '522002002', '502604092', '502016832', '512921022', '501689362', '501487042', + '501871802', '531141222', '506508582', '523021042', '531103762', '503995752', '502504322', + '518111442', '502025912', '531139082', '501813322', '501713972', '501670912', '501696822', + '501900322', '502876262', '505048082', '501804722', '507032262', '531063752', '505062832', + '531043212', '505612972', '501889442', '502048572', '502587392', '502597282', '525001462', + '501717072', '502861672', '502613792', '501495282', '501766952', '502613822', '502014112', + '502589832', '505067362', '503019052', '502880332', '531118832', '505614622', '531038812', + '531043492', '502549512', '523003672', '502026722', '501930752', '501933662', '501933832', + '503996902', '531129222', '502594232', '502861222', '502557132', '531036712', '502539762', + '501834672', '512922002', '505050982', '531128862', '502003552', '531128072', '530001622', + '502501862', '524001532', '519510552', '531063922', '502856032', '502612982', '506125772', + '531038332', '502535252', '531037972', '524008812', '501880712', '501881042', '531033942', + '501812032', '501786562', '502613962', '501908862', '502861532', '503004012', '502573812', + '531038162', '531021162', '502500262', '502569432', '502569602', '503002362', '502573502', + '503004802', '502614152', '531139902', '502055412', '504681602', '502574142', '502507992', + '531050162', '503014212', '502558252', '502569882', '502570112', '502609612', '501252592', + '502569122', '531044952', '502575572', '503510422', '502861702', '531033772', '502569262', + '505039652', '531041902', '531038782', '503501342', '504073602', '502602022', '531032962', + '531038472', '522013322', '502613512', '531113822', '531036682', '502613482', '505013092', + '502613652', '501807802', '502571852', '502573162', '531033012', '523020062', '504004082', + '504017532', '531057612', '502559062', '502618662', '505620142', '505038362', '502572972', + '502575122', '503508312', '502517262', '501966242', '531133572', '502560842', '501467262', + '502605382', '502054432', '503008972', '502571372', '502568452', '505012622', '503015812', + '503018102', '503512042', '505036262', '504061302', '505040022', '505045032', '504062562', + '507033102', '502895682', '502602332', '502615132', '502858272', '505038532', '505038672', + '502558392', '511100972', '505628852', '502514972', '502572352', '531039142', '502572492', + '523006752', '502574452', '502896522', '503000092', '531044162', '503510902', '503511062', + '524009452', '504015742', '504064972', '504065022', '505626272', '506008652', '507030502', + '503510872', '531064902', '502861842', '505030302', '504065502', '531129532', '505037722', + '506120592', '504688062', '522002142', '505069462', '501490272', '502568592', '505031112', + '502570732', '511083342', '503973252', '509500512', '531009162', '503974852', '503975212', + '504065472', '502570872', '506118652', '511009932', '510902162', '502560192', '502861982', + '523004032', '511077652', '506012072', '504065782', '531114012', '502560052', '505004182', + '511081242', '506118172', '523003842', '502594372', '531020212', '513004642', '513004952', + '531119332', '510001092', '504688402', '509503422', '531121612', '509006682', '514008772', + '531057272', '514026622', '501801022', '506006072', '508020992', '531041252', '506116722', + '514010432', '522000972', '515007272', '523003982', '504010562', '513008842', '504062082', + '515005342', '507029512', '515004532', '523024602', '508022812', '523022782', '505614762', + '515119402', '514029532', '531133602', '512000342', '520001582', '514027432', '531046122', + '505001302', '504683702', '507000042', '523004202', '505614932', '503997062', '517008282', + '517008452', '517008932', '507005192', '519006102', '503016282', '513000132', '517026442', + '531128102', '510006442', '502862032', '514025952', '501429182', '513007102', '501430482', + '524001702', '509502922', '512032392', '507004552', '502579322', '515000782', '504068102', + '509503112', '511090012', '509503252', '515116462', '515117612', '512034972', '502857292', + '506003472', '509501292', '512006952', '510300502', '518110772', '518110942', '505068512', + '519505672', '518007262', '531065232', '512034662', '519501952', '512034832', '531042372', + '517000672', '517025942', '517021572', '515119852', '517025152', '524001672', '519006382', + '505618652', '513006912', '505057502', '512040212', '502862342', '515121792', '516000262', + '502035632', '502400012', '531043182', '501407642', '501211802', '504687872', '505614592', + '517021882', '517022382', '501046852', '518004492', '501310312', '502880782', '501486542', + '502018452', '501318402', '512041472', '518111752', '505001612', '508005012', '511097572', + '502886012', '522006512', '506010452', '502051662', '502038682', '505010972', '510003192', + '505625632', '518005782', '501016732', '502533322', '508000142', '505008692', '502537972', + '531057302', '502894732', '502049722', '501439412', '501448632', '501453202', '501545492', + '501552952', '504011232', '503502802', '503515882', '517007502', '501910662', '502863152', + '523004482', '504000052', '523004342', '501361162', '501149732', '514026452', '524001842', + '505615122', '501500812', '531132592', '501496882', '505035592', '502618352', '504070352', + '501406832', '501406972', '502589212', '501416242', '502862512', '502863462', '531068002', + '523005292', '501243852', '501478922', '506506172', '502595492', '531066042', '531057582', + '523005632', '507029172', '501997962', '531054362', '501087192', '502593082', '502862652', + '502602472', '505063952', '505613022', '502602502', '505060872', '501405852', '505049712', + '505615262', '522002452', '531066352', '501478892', '531121442', '511101332', '514035982', + '524002172', '518107062', '531069092', '518113542', '524001982', '501617752', '524010272', + '502863322', '501399992', '502864442', '502862962', '522002932', '502589662', '504681882', + '522002592', '531135532', '523022472', '523005152', '523006132', '504681912', '501681752', + '502863292', '531065992', '523004792', '502864612', '514033572', '505615092', '524002032', + '531067472', '503504872', '502864582', '531094682', '524002482', '503997542', '519507632', + '502592892', '512922142', '522003092', '522002762', '505626442', '502604572', '503997682', + '531067502', '523005772', '531067812', '512046342', '501971602', '531068142', '531068932', + '501401172', '501476962', '502616732', '531067782', '531123852', '501433082', '511101642', + '501429522', '501388702', '502865082', '512922312', '502864922', '503513952', '523005322', + '523005462', '505615572', '503997852', '501690492', '505615602', '502865562', '524002652', + '505615432', '504682072', '525001802', '502890532', '524002512', '502865902', '503514312', + '531066212', '502866712', '509500172', '502865112', '502864752', '531094712', '501917462', + '523006302', '502619022', '531069602', '531070392', '531130522', '531070422', '531069432', + '522003122', '516017742', '502583842', '522009562', '502866062', '531129192', '512033372', + '523005802', '531067952', '502863802', '523005942', '531072042', '504073882', '531019532', + '531044022', '501766502', '501769412', '513022012', '501961682', '502866372', '502895712', + '502579942', '531120462', '502587252', '531130972', '523006442', '502586612', '531059542', + '502865872', '525001942', '503514622', '524002822', '503975492', '506122102', '531069122', + '503997712', '531055202', '502587112', '501491252', '513022322', '504074552', '531070732', + '502865422', '501135162', '502585152', '502864892', '523006612', '501436162', '531011922', + '502865732', '502586752', '502618042', '501759492', '501424962', '506510382', '501949852', + '531059712', '502582692', '531071852', '531008212', '504073572', '523001742', '522011532', + '519507462', '501824812', '504686442', '505622242', '502587082', '501792392', '503507812', + '502588852', '505615742', '501783482', '502866402', '502589492', '505054252', '531071062', + '531070872', '502589182', '502589042', '501784462', '502588542', '504072762', '502590342', + '502587422', '502587562', '505048902', '531020352', '522008752', '501775722', '531103932', + '531070902', '503998042', '502588372', '502588402', '505051032', '531021952', '531020662', + '502893752', '504070182', '531042062', '502574932', '501494162', '502881312', '503972612', + '507031622', '523013392', '503517842', '505056492', '501411712', '503972582', '531096162', + '501780882', '501779922', '515128762', '531008042', '502590792', '502614802', '502591322', + '502587872', '505052462', '502587902', '503507502', '502592132', '505053922', '522003432', + '505056182', '505069322', '501711422', '515118872', '505051202', '505051512', '505055402', + '507883432', '502880812', '501405712', '505049992', '501404392', '502898452', '531012902', + '523022502', '505052772', '505052802', '502590512', '522009732', '502590172', '531028302', + '520000012', '505050672', '510305482', '510303102', '510303242', '503515572', '502591942', + '506123532', '506123362', '505050192', '505052152', '531039452', '531118972', '502590032', + '505052322', '531009502', '512921472', '501424032', '523020542', '505053272', '524006852', + '512924382', '512045222', '531018722', '531008832', '506124962', '501402152', '514035052', + '519511672', '501410392', '501402772', '505055232', '531018692', '505623842', '505063812', + '502603312', '523000282', '511099702', '501429492', '502856172', '506123672', '512043432', + '502591292', '519507772', '523023622', '501897852', '502591632', '505050532', '501405992', + '501424822', '502856482', '502591802', '502889542', '501452362', '531136792', '505053302', + '511106822', '512043882', '511098862', '512041812', '505052942', '502592922', '504070972', + '501898352', '505626302', '523006922', '501419772', '501456252', '531132142', '501450572', + '523023282', '525000652', '502593902', '531062802', '501999752', '501414592', '502618832', + '502888392', '502599382', '501405542', '502616082', '523006892', '515123582', '510303382', + '510303412', '506507462', '501412832', '505056832', '503973082', '531037042', '505054732', + '501430822', '504070662', '501410112', '506123702', '503515262', '501383522', '502593732', + '501421602', '504071022', '512922452', '501413952', '502603142', '511094972', '507031932', + '504070522', '502858582', '502880472', '518113712', '512041502', '525000962', '502616112', + '502592752', '501414622', '523003532', '522007632', '512041782', '531012082', '531105722', + '502596332', '501996362', '506124202', '523023312', '510304052', '523013422', '502574312', + '501466762', '505057022', '501417192', '512921642', '501419942', '507032092', '519513462', + '512923262', '502599242', '501439102', '501447652', '501451412', '502597312', '503998352', + '501493182', '501493972', '501421432', '502888422', '502570422', '512045362', '501457232', + '501494022', '531020182', '531131782', '501417842', '501784012', '501484752', '502572042', + '511095502', '514031162', '511105532', '501415262', '501451072', '502597452', '505068482', + '502603282', '502858442', '501419322', '501453032', '501451862', '504682242', '501460152', + '504071812', '501896112', '502591462', '531132282', '501452192', '531137012', '501467122', + '501452842', '502602952', '502598122', '501452222', '501483152', '502614462', '511094832', + '501966412', '501432612', '501450432', '515123272', '505059602', '501469672', '501440232', + '520003062', '505059092', '505059122', '520002252', '501484132', '505057952', '531056292', + '531106052', '503518342', '502616422', '522011052', '511095472', '502600542', '515122942', + '502855672', '502600992', '502603002', '531014832', '531104572', '501487522', '501488332', + '505060392', '514031952', '504072932', '502880502', '523024742', '501487832', '512043122', + '502901992', '501488022', '512043912', '501490892', '501464042', '501464212', '531050782', + '501464492', '501490132', '501462422', '524010752', '531073022', '501458182', '501367942', + '501493212', '501954902', '505067842', '501487182', '501487212', '511094662', '531034132', + '503973872', '531054192', '501455302', '505059572', '511095332', '501471332', '531045932', + '501496262', '531040302', '501021272', '524008162', '501497552', '501885552', '502572212', + '502572662', '505060082', '505060112', '501456082', '531574692', '502614012', '502598742', + '507032122', '531010012', '501495452', '501494812', '501494952', '502600232', '501492852', + '502600402', '501487972', '525001012', '505070282', '501496092', '531017742', '531104602', + '502594062', '507032742', '501498192', '531576792', '502601492', '501497242', '531012252', + '501489282', '531036232', '501999892', '501489142', '501489452', '511096932', '518114182', + '523000932', '501492992', '511097432', '502866542', '531576482', '531018242', '531573092', + '501486852', '505060732', '531046742', '502588062', '510902472', '531007682', '531007712', + '531574102', '501498052', '501499342', '504072622', '504073122', '502602642', '512046792', + '501498222', '502855702', '511099672', '501497692', '531041872', '502586582', '502601042', + '531575702', '501498532', '501851712', '531052262', '502052472', '507032432', '502895852', + '531034302', '502600372', '504073262', '505059432', '505060252', '502588992', '531128412', + '503507162', '522000182', '531009812', '531013682', '501907882', '531016622', '519513322', + '531006872', '510900682', '531006902', '502893922', '502538472', '531016762', '502603762', + '531040442', '531131812', '531095182', '531011132', '512044862', '531009642', '502560532', + '518114212', '501495312', '531008352', '510307612', '524011872', '504073912', '512045052', + '505052012', '505052292', '531013852', '503996252', '531039622', '531010772', '531007992', + '531010632', '531089352', '531010322', '531007232', '501497862', '502591772', '505053752', + '502593872', '501856722', '505053892', '531015502', '531009332', '502614292', '502608152', + '511098072', '511098102', '505063162', '505064932', '523023762', '502605072', '502606192', + '505063782', '512044692', '531014212', '514032622', '531015782', '502606672', '506124792', + '531012392', '502896042', '531012422', '502614772', '505064622', '505064592', '505061852', + '531015952', '531016002', '531011442', '531048192', '531024552', '531010152', '531009952', + '501767002', '531016142', '502617682', '502605102', '505062212', '502604122', '501944702', + '505064282', '512044072', '502608632', '503973392', '502604912', '531014662', '502603932', + '502856652', '502616392', '531045282', '531014522', '502606052', '531095212', '531134722', + '531051002', '531016932', '505063022', '531015162', '502605412', '512036002', '531016452', + '502605552', '502606982', '502605862', '502860102', '531024412', '502606362', '505065122', + '502608322', '531025222', '502617062', '502898762', '505064002', '531026032', '531026652', + '505065602', '505065572', '502608942', '502606532', '505065432', '505064452', '524000382', + '531022282', '531020492', '503517192', '502609752', '531026512', '502606222', '523002692', + '502607792', '502607202', '531025192', '505063642', '531019702', '505066102', '502604602', + '509505212', '523001882', '505063472', '502603592', '505065262', '502607172', '531095492', + '531017432', '531019672', '502607962', '502607652', '523001262', '502607032', '531049652', + '531055652', '531050472', '531024722', '516019192', '531025982', '502857152', '502607822', + '502608802', '505064762', '512045842', '531056152', '502859732', '531060222', '531051282', + '505614002', '502857462', '502859902', '505063332', '531044472', '522001502', '502857802', + '523002722', '502609892', '502609132', '502608772', '504073432', '502858922', '501949542', + '502859872', '520001272', '503995922', '511099842', '523002862', '531060532', '502609582', + '523002552', '531024692', '502894392', '531023742', '531023262', '505069292', '502857012', + '505616722', '531048532', '512920832', '523002102', '505613952', '531059682', '502857632', + '502858612', '518115472', '523000762', '503996562', '502857942', '505070592', '502856962', + '531055172', '505613162', '502901852', '531054842', '523002412', '522001162', '520001302', + '502617402', '522000492', '503518482', '522000522', '531056012', '531049792', '502617372', + '502617992', '531049512', '505063502', '524012992', '531056632', '505613812', '531046572', + '509505662', '505065742', '502610262', '531023432', '531058902', '502857772', '531049822', + '505070312', '524000242', '522000832', '502856512', '503995892', '524000692', '523003192', + '523002242', '515121032', '518111302', '505613782', '515124422', '502615612', '531058112', + '524001192', '531061962', '531061202', '525001632', '511100832', '514033262', '501423982', + '523003362', '502858892', '531057752', '502858752', '505614282', '503996392', '512921782', + '531048222', '507284172', '502859422', '531062322', '502860552', '505613642', '531061342', + '505614452', '523002072', '531058872', '504070212', '502859082', '531129672', '523001912', + '502859562', '522001332', '531058082', '502860862', '531056772', '505613472', '531062292', + '514033092', '531059062', '505613502', '501514732', '524000722', '523001432', '523002382', + '522001952', '505617192', '531073502', '502618522', '531062152', '522003602', '502867042', + '531062632', '503996732', '526000892', '504685802', '505614142', '502888872', '531069262', + '531061482', '505058452', '502860722', '502608292', '503974232', '531072972', '502863012', + '522001812', '502860692', '506505842', '512921952', '501737472', '502860412', '501767932', + '502866852', '501490302', '531062012', '523003222', '522001782', '522001642', '524001052', + '531032342', '502867492', '503998522', '531075742', '523007872', '505007122', '502890702', + '522003572', '531073472', '502856792', '502590482', '511094492', '501407332', '523007902', + '506509082', '505070142', '501483802', '501451242', '502592272', '501516212', '502867352', + '502866992', '506509252', '507033412', '531574242', '505058762', '531110572', '522001472', + '505614312', '531015332', '523007112', '522003742', '531128552', '505620312', '523007252', + '511100522', '523001122', '502867662', '505615882', '503997992', '505615912', '502591152', + '505070452', '502590822', '505064312', '531073812', '503998492', '502601972', '503998972', + '502868812', '531077532', '502885822', '531007402', '531108322', '502867182', '524003802', + '531076552', '510902502', '531074932', '504073092', '501438122', '503974062', '502886152', + '523008402', '524003292', '523007732', '503998662', '531076382', '522004102', '523007562', + '531010802', '514033882', '502896352', '502573022', '502617232', '531039762', '502867212', + '523007422', '531078652', '531140102', '523008232', '522006652', '507033382', '523008992', + '523008682', '531079802', '524004132', '505616242', '505616072', '531079942', '504682382', + '531077222', '531074452', '512922592', '524003772', '523009492', '523008062', '503973902', + '502869622', '523009212', '522004552', '531080002', '524003462', '514033912', '512922622', + '502868502', '504682412', '502868472', '531076692', '505616552', '531076412', '511101952', + '531076242', '502876572', '502868782', '531075882', '501416102', '505616102', '520003372', + '523008542', '531078792', '520002392', '502868332', '524003632', '501484612', '531130662', + '531077362', '502867972', '502868022', '522003912', '502868162', '523008372', '502867522', + '531074762', '524003942', '512922762', '522004692', '531079772', '503999022', '502869592', + '505616692', '512922932', '506127112', '502870752', '522004412', '522004242', '502869762', + '531078822', '531134692', '522004722', '511102452', '514034102', '502870442', '531080282', + '506127562', '531081742', '523008852', '502869452', '504682552', '502870302', '524004272', + '502870132', '524004582', '531079152', '522004382', '505616382', '523008712', '503999332', + '531084512', '511102622', '502870892', '524004892', '502004222', '523009182', '531082722', + '531081262', '523007392', '511102592', '501419802', '523020682', '531081572', '501132252', + '503999502', '511102312', '531081602', '502871082', '505624032', '531080142', '531081432', + '531080312', '502871732', '531083362', '511102762', '505062182', '530001762', '531084482', + '506506342', '524004442', '505616862', '520002732', '505617052', '523009972', '523009832', + '531082692', '503999162', '524004612', '501971262', '502871112', '506127902', '531017092', + '531081882', '506506202', '523010512', '504682722', '531083052', '502871252', '502871872', + '531082862', '524005082', '511102932', '531081912', '522004862', '531084822', '531085322', + '502872402', '505072692', '505071122', '502871902', '503974372', '502871392', '523010342', + '502881002', '503999642', '523010482', '502872062', '522005192', '501523162', '502895402', + '505617362', '522011192', '522005052', '523009662', '502870922', '523010962', '519510862', + '504683052', '531084342', '505072552', '505061682', '531083222', '531095662', '501094822', + '502865392', '522005222', '501505482', '503999782', '502872372', '502873212', '531085292', + '505073982', '505074032', '502871562', '505068172', '523011012', '523010032', '531086302', + '505071602', '502871422', '523024122', '501374302', '511103602', '523010822', '523011152', + '531088062', '524005732', '524005392', '503999952', '531087872', '505617532', '514025162', + '505623982', '505617672', '523011292', '531087732', '502872542', '531086132', '503970032', + '502872682', '531084792', '524005562', '531087422', '502873492', '523010172', '502873352', + '524005872', '501443762', '524005422', '531087392', '523011322', '524013832', '531085462', + '531088402', '502872852', '522005532', '502872992', '512037432', '505618032', '505074172', + '502873182', '504683222', '507033722', '511103742', '523011942', '502873972', '531088372', + '531084962', '505072862', '531089832', '505617702', '504683192', '501523502', '522005362', + '523010792', '523012272', '511104862', '502873832', '531089522', '523011772', '502874332', + '503999812', '505625942', '512923122', '522005842', '530002432', '511097882', '523011462', + '502874022', '502872712', '522006032', '523011802', '502873042', '502888902', '507883122', + '506506512', '505072412', '505071572', '505071432', '522005672', '531058732', '531087902', + '524006062', '531092302', '513023582', '511103912', '504683362', '502874502', '523011632', + '505068342', '531088542', '531090962', '503970482', '522005702', '502875282', '531090342', + '506506482', '531088232', '505617982', '502873662', '523012892', '511103882', '531092922', + '531088712', '506506652', '515127812', '506506792', '523012132', '531090202', '505617842', + '503970202', '502873522', '510307302', '505618512', '531093112', '531092892', '531091322', + '501453792', '531091012', '505071742', '502601352', '522006342', '505618482', '503970512', + '505618172', '502874642', '505071882', '501691812', '502875622', '523013082', '523012442', + '506506822', '523012922', '502875312', '523012582', '531089492', '503970172', '502875762', + '531090172', '523012612', '522006202', '502874162', '502874472', '531088682', '511104072', + '505618342', '506506962', '511104102', '531133742', '531095832', '531096502', '504683532', + '531091292', '523013252', '524006542', '531094062', '502876602', '531136652', '531094232', + '502876092', '504687562', '503970342', '502875452', '502876122', '531091152', '531019842', + '523013112', '531093082', '531131952', '502876912', '503970822', '531095522', '502877552', + '522011702', '525000032', '505618822', '504683982', '524007042', '524007182', '504684962', + '523014062', '502876742', '522006822', '523013732', '505618792', '505622552', '523012752', + '531097592', '502896212', '504684032', '531096782', '531096812', '506510242', '505618962', + '511104382', '503970792', '503971012', '531099722', '524007212', '502896182', '522007012', + '531105552', '504687732', '502876882', '523013902', '531097452', '505619012', '502877722', + '502877382', '502894112', '523020372', '523013562', '504683842', '531098882', '512923432', + '502877862', '502877412', '520002562', '523014852', '522006962', '531096472', '503970962', + '531128692', '524007352', '531098912', '518115332', '506507152', '503971322', '512923572', + '531100852', '531100682', '531129052', '531098602', '503971292', '502877072', '503971152', + '523015212', '502877242', '523013872', '531100992', '522006792', '505619322', '523014712', + '505619152', '531102162', '504684512', '502878192', '530001932', '501661972', '523014402', + '501579222', '531098572', '531100712', '502879342', '504684342', '524010442', '516018692', + '525000172', '502879512', '502879172', '531102782', '523022952', '531101182', '531102952', + '523015042', '505619462', '503971462', '512923602', '531103002', '523014682', '504684202', + '531099862', '505619292', '511104692', '522007772', '522007462', '523015522', '531102022', + '531103592', '514034382', '502881142', '531115612', '511104552', '523014542', '530002742', + '502879962', '516018722', '531101212', '523015182', '502879652', '531108012', '523016502', + '501682112', '501454152', '531107172', '523015492', '531106222', '531108632', '511105052', + '506507322', '502878222', '502881282', '524008642', '514035222', '523014992', '523016022', + '505620282', '502880022', '504684482', '531103622', '504685012', '502880162', '526000132', + '531107202', '502881452', '506510692', '531107342', '502881592', '524009002', '522007942', + '502882572', '531106982', '523020402', '503971772', '526000272', '523017002', '531106842', + '523016162', '531108292', '505624202', '530002122', '501825002', '523016332', '531108152', + '522008302', '526000302', '524008782', '502046502', '503971802', '510901352', '531107032', + '502882882', '525000342', '523016952', '501488502', '512923742', '502883102', '530002092', + '504685152', '531109302', '525000482', '522008272', '531109612', '520002872', '523015352', + '524008952', '505621572', '519511702', '531014492', '520002902', '523016812', '505621432', + '523016642', '502882742', '531108462', '505621092', '501406492', '502883412', '501455132', + '511105192', '523017592', '501075682', '501401202', '506507632', '523017312', '505621262', + '502883692', '530002602', '502883072', '504685322', '531050332', '523016472', '505621882', + '501483632', '531128722', '531020522', '501495142', '502883242', '502889232', '507034052', + '524009142', '531110742', '523015972', '531109442', '502885792', '501610472', '523017282', + '502883552', '504685462', '502883862', '523017452', '501522182', '502884052', '531132312', + '514035362', '531018552', '501420452', '524009312', '505621912', '506507942', '524006372', + '502884672', '502884842', '523017622', '522000352', '531112842', '502884192', '531112982', + '524009282', '511105222', '501723552', '502885202', '501448012', '502885342', '505622102', + '502885032', '505622412', '502884532', '502884222', '501494502', '522008612', '505622072', + '531112052', '502884702', '502884362', '531111072', '531112532', '510303552', '502886292', + '504685772', '531114322', '520003232', '523018912', '503972272', '502594402', '531114292', + '501634312', '531114152', '502885962', '504685632', '531114462', '531129842', '519512172', + '523018262', '531112362', '526000582', '523018122', '512923092', '502885482', '522008892', + '523018432', '531120322', '523018092', '502885172', '504685942', '522008922', '503972752', + '505623192', '502886322', '531134862', '531116562', '531132002', '520001752', '524005902', + '531116732', '523022642', '531115582', '531116392', '518113852', '512923882', '523009352', + '506508272', '506509112', '502886462', '502886632', '523019072', '531116422', '531115442', + '522009112', '502886772', '502887892', '514035532', '531117682', '523019382', '523019412', + '511107012', '502887132', '531117712', '505623222', '531123712', '515123302', '502887442', + '525000792', '502887302', '522009872', '502887752', '531117402', '523019722', '530002572', + '522009082', '510901042', '531113652', '523019102', '523019242', '531118492', '512923912', + '505623672', '522009902', '502889062', '511105672', '531117992', '531117372', '502887922', + '523019862', '520001922', '505624172', '523019552', '522009252', '531117542', '531117852', + '524010612', '522010072', '502889712', '526000612', '519512202', '520001892', '502887612', + '505623362', '531124182', '522010102', '505624962', '505625772', '523021522', '526000752', + '531121752', '502891032', '502889682', '505625012', '502891342', '502891172', '502891202', + '511106172', '514035672', '503972922', '507034222', '531121892', '523020852', '531123992', + '502887582', '523021182', '506508302', '502890842', '511106202', '531121582', '523021662', + '516019052', '502891822', '531122872', '502892632', '502891512', '502891652', '524010892', + '502892152', '531123682', '502890982', '503973422', '502891962', '522010242', '506508612', + '523020992', '523003702', '502892322', '504686752', '531124042', '531122902', '523021352', + '522010382', '502890672', '502892292', '522010722', '514035842', '502892462', '505625322', + '523021832', '524011252', '522010692', '502894422', '504686892', '531126312', '524011112', + '502892802', '531124352', '531126762', '531125162', '511106512', '502891792', '508021972', + '531125952', '503973562', '531126002', '507883262', '531125022', '531087562', '502892772', + '502892012', '525001152', '505625152', '525001292', '523022022', '531124212', '501809562', + '525001322', '502893272', '523022162', '502893442', '523022812', '531127742', '504687082', + '523022332', '502895372', '506508922', '531126932', '522011222', '531126142', '522010862', + '531126622', '504687112', '502893132', '519513292', '503973732', '510902332', '506509732', + '502896972', '531133262', '512924412', '523023592', '505626612', '531132622', '523023142', + '531126592', '505626132', '514036172', '502897022', '502894902', '502897642', '523024092', + '531132452', '523023932', '531136032', '526000922', '531127882', '522011672', '502898002', + '503974682', '524012372', '501487662', '531135982', '502898282', '502898142', '506510072', + '502897162', '523024572', '502898312', '522012032', '506509902', '511107292', '504687902', + '523024262', '531136172', '502897782', '522012172', '524012542', '512924862', '502899092', + '531136822', '511107632', '531135702', '502899262', '522012342', '511107322', '505626752', + '531135672', '502897502', '526001082', '502898592', '506510102', '522011982', '505627112', + '522011842', '524012852', '505057642', '524012712', '523025242', '531138132', '526001112', + '525002132', '502898622', '523024912', '505056042', '504688372', '502899912', '504688232', + '502900422', '522012202', '502899122', '531136202', '524012682', '505627422', '511107772', + '514036342', '523024882', '523025072', '502900112', '502900732', '519513632', '516019532', + '502900392', '531137152', '501407022', '505627902', '502900252', '531137462', '511107802', + '531137292', '523025382', '523025412', '502899572', '525002302', '505627562', '502899602', + '522012652', '504688542', '520003542', '523025862', '505627732', '502899882', '505627872', + '522012512', '531138302', '519514132', '531138582', '502899742', '524013042', '523025692', + '512925052', '525002272', '531138442', '531138892', '502901542', '522012962', '502901372', + '502900902', '522013152', '502901232', '504688682', '502902042', '523025552', '502900562'] + + +def is_identification(value): + """ + Valida las identificaciones fiscales de la República Dominicana + Cédula de identidad personal y Registro nacional del contribuyente + :param value: recibe una cédula o RNC + """ + if not value: + return False + if len(value) > 0: + value = value.strip() + if (len(value) == 9 or len(value) == 11) and value.isdigit(): # Valida logitud y Valida que solo sean numeros + num = list(map(int, value)) # convierte string en lista de string + if len(value) == 11: # si tiene 11 digitos es una cedula + if value in excepcionesCedulas: # valida en el listado + return True + else: # valida el algoritmo de (LUHN) + return sum(num[::-2] + [sum(divmod(d * 2, 10)) for d in num[-2::-2]]) % 10 == 0 + else: + if value in excepcionesRNC: + return True + else: + pesoRNC = [7, 9, 8, 6, 5, 4, 3, 2] # Mod11 pero el RNC utiliza su propio sistema de peso. + resto = sum([num[i] * pesoRNC[i] for i in range(len(pesoRNC))]) % 11 + digito = resto if resto in [0, 1] else 11 - resto + return digito == num[-1] + else: + return False + + +def is_ncf(value, type): + u""" + Valida estructura del Número de Comprobante Ficasl (NCF) + para República Dominicana. + + Caracter 1: Serie + Valores Permitidos: A o P + Caracter 2-3: División de Negocios + Valores Permitidos: 1 al 99 + Caracter 4-6: Punto de Emisión + Valores Permitidos: 1 al 999 + Caracter 7-9: Area de Impresión + Valores Permitidos: 1 al 999 + Caracter 10-11: Tipo de Comprobante + Valores Permitidos: 01, 02, 03, 04, 11, 12, 13, 14, 15 + Caracter 12-19: Secuendial + Valores Permitidos: 1 al 99,999,999 (sin comas) + + Tamaño: 19 Caracteres + + :param value: string con NCF + + :returns: True cuando tiene exito, False cuando falla. + """ + if not value: + return False + + if len(value) == 19: + try: + if type == "in_refund" and value[0] in ('A', 'P') and int(value[1:3]) and int(value[3:6]) and int(value[6:9])\ + and value[9:11] == '04' and int(value[11:20]): + return True + elif type == "in_invoice" and value[0] in ('A', 'P') and int(value[1:3]) and int(value[3:6]) and int(value[6:9])\ + and value[9:11] in ('01','14','15','11','13') and int(value[11:20]): + return True + except: + pass + return False + +def _internet_on(): + try: + response = urllib2.urlopen('http://api.marcos.do/', timeout=1) + return True + except urllib2.URLError as err: + pass + return False diff --git a/security/ir.model.access.csv b/security/ir.model.access.csv new file mode 100644 index 000000000..019c1efb4 --- /dev/null +++ b/security/ir.model.access.csv @@ -0,0 +1,2 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_ncf_manager_ncf_manager,ncf_manager.ncf_manager,model_ncf_manager_ncf_manager,,1,0,0,0 \ No newline at end of file diff --git a/static/description/icon.paint b/static/description/icon.paint new file mode 100644 index 0000000000000000000000000000000000000000..f48b1683e10b7cccf791ca385f07f0bea62db46c GIT binary patch literal 354513 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 literal 0 HcmV?d00001 diff --git a/static/description/icon.png b/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..7cc966a10f588c4f0a9f41f361b792b842f56df8 GIT binary patch literal 11234 zcmch7RaDzw@FxE>eV;B13}0*Z~*GqtDDBYV@~ZpXwtq2nwN?olR*&w~F&U!b6= zJ!o}R=zXe^5S(%6-R03l`p5)`zvH1mLr&BG^xcTa*lKTj@s;wF?QN2-lis!p;Bbla8vSaxJSfCN^mBG zMdVXr78q#Ja+;Q_q6uj{^KLd?llksvzBIS`m{;8*PfN}KrMGJQQ}!$}9`$dj-k2Ba zhucJY$vq>wORJrh*WiAh53TFg3w`cy0%IL)x41TDA~E!I93#_+;(rWW%j4a>uA4W{ zS99F2*g+IpWqGdV*Sj8DKITDs?ZeL?eWjBbd2hmwElut?1zEP&wfa>mlJey5u{WF` z0Wg~1%c)t3#FCf^E59;pONftUO{$i$D!THDs+*%<25yH5opEh&M@|ZFkLvU;R#to& z$nN#(ZxJ*)Jrx7=M70}do(#F&L3DCYw-I}SnPYywviG;&d=)PP-10;Ycl~$H!cw`h z?u*lsL&^I?BZCU$r7pKO9_v?>iO`=rQX`atfJt8ojCnRqAtgJLDtMH_v-Bzy`?n$5 z`A@QqpWT9M4KXXVVq!Jm+--aNH(CP|l>T}C;X;)mL0$NbI2i2dyb+zaYH!hr-?4)+ z*C-b4%R>n;`N*oto=2M(*iydO?h!u~ZORz$?fRblI6Q0~deb`5#PKfT%}0s`>if7V ziW!@e%5W2FQprFrVKhnB=L{rEp_68ON|%XHK2%2EXmPd6FEkCL?~ZR5m7V3x#D(V{ zzQyosmt@JUiZpfPTRib|sG8_3RB;i&J~55Gy2CK-ydN~LH`(P9)`D$3&uKT9xG}%k zzvsK;ymu`69kgX|(&9E`^rNFeweP_Bt-H4#H3V~$;_p<(K-2*38T4e!fKWa#btGfE z_S{t5&;>{!UzLV&>v*I8Ts>B{>OAy3qKT`MEr9)DuyZ_MG+;bnBB0k4Em3SNXR~V^ z=t}QyK6vSPMs2j_N8wA=Vz-;>RHgiq$(fvv!tJ}zx|Sv1DH|TPwY1h^NNV-|9Yr2b zt^_TCHT|E`JCiP;K5EomJ=(e2qn@7bu50ouT74HaP?r>G471F;m}DW{t5$=v0>%^# zgth~bj+hQ zsU}CUeqn&l!r%?dy|jsn{?C5TUL9O^-}3ncKFsc}^t)GWa|bsIi{*5$9ed2b0Y6ou z*u@5+=I8nAX<9tGprEiND9K9c`k)@>+@;M9rVQ22o;jQK-AUlpE?S5M_YPu;#Vo0* z3yGYFElK!o2;Na1Y_(*Ass>(8jJSy1JmWLFJ6MX{Ob0D3j)-3V#u~J6&i!L!fyql} z)`!Rcqr=#*GU8K)ymm`bQaf`1BD6zKTW|F?`F6sfxRQg>nE?gu|KUf(iDpz~6gn7; zg5An$LACrs>8pI~xqRZ%Y}w}@2Z4MSw4YAC2PT%lF^7tc;(O?bu%o~emc{23u}yL? zJ=^h|k_H{SiD)D7z=XNYnyn&kq+CvZFBTguRaUBRZE~R1pAl@|iG-V5Y`+^NJ^0EUtM-|aS z(up*sAch6!l|A{bRb`C$04r%QMzc-$bnSm2(MS+fR6Qh!QOkcIj7ShnfUcggzWpl4 zKagl7NF-@w5|S{)mVY^hvV~fINS2x`jrMs_C^!s9RtaKgIe$L;nGttwFI`b(i54u1 z9Sj!0!EV<3QgiJRn9sl~cBw(+RW?aNV0+(eOIVfhJN~01; z`xXNa74qcWXRc3ZUaUoz6ob-$z))m}TTEic7A-j{5B?ac{)q5~K)o!~9aTIqXwn!dnSY>!Bw^c^ zyquU32++!=P5%NXIgHpOA*%}k1F*MdDa5MkO9rPx$aRbhA6!kWYtj6%P*ZSLkjlk6 zIP!nG^$k#`!Qj_u`i^6x{bgdj{7#Q>j`x*y)vhMtvUP-L93-xq#`=Hcr$VBJpK6@? zKI!@4&is!|$e1)M;0=6Y+5|s-2z@3ZsPrN$>4lP$^uEC^lkX`;4w-vm301)OyuW?1 zpQA{`9I{cs+-1l|L6t;O^M4}Xx@*vD>zXksWiNR1RGQis%m6@Zm`hC=F{4TM*|Yn`J{8iYYg|a{YNFp6B|xMZ z0tP#dRXF%!30<`-ZHp7Vf{HKCtXz`{Cf=JlE!8K%DxmsM00KA$Po z`5>KXo<29c6=XG|R_{@)gdQs6ff&`>+ut86-V7Rf$y)(yf*v!cR$hD$Ax4lbs`K&7 z4^>J!bK8!M4_|SMQ+B~!TSlS+?pr)E9i<*UB ziE4)Qed?2sP6O-3<)yZU1|dE^;Y@gKcS-F;PU#sXu#V{kRZz{4#G{k)#l?kg?_uXI zzeMVmV3s^n2429$oCr1Dgbz1!?v0yc(cNH5kUP(uleD=?qnQxp#A2uZ)B0t^T z85%FT?81%U9knUHKI~iHeKDcQ;IjitJbblNqNirWw9C+$y*W9`IS)EMXb?XVs(%j` zXuo?g=5W)%A`shMI^*zIK%m2#Rm}C|>bEFm2od!oO*HTanzSbDgkbHQBE~$9DjUyl z9??B+rUfgVSxWWv3}(E$u1qtHjAaa8P_R(`S?`Y?Ro?p}NmSc;Dd~aW(6yl}PfZF`vx@bJ3qNAZ1d$;2KQu@<%Ho13g0V!KokxnTvUhK$*ZR~wR ze2xubIPx&QIVnh!c|G1#tv&U~yd&H!Vp8%*#tAr-*^PL4Vf07gBqwuHTkB&|; zo-JLso)+k)y3>^sEgW3e+7v^27NH2>8uy89Qq988z86B%ZSFx<3KSFJ6M=IJ`rT!@ z)WVYTXLgf?fnLcAwO7|1Jb8xff+svF6(tw@!qXcppB?P>BRuxbm+oKHyct+D5)m$7 ztzTv3y=z;@YSo}vRo8mTBEHv9Re z^0sC0>o_LAv3LBlew&zT)md^H;wPYh{V6?hb(?^F&W`uVlz1WSY-?rK#V!Dy4}Xhs zvHo1XDx$AU_BJVm1_V4bd96}sinUB=7^6?#{r&o4w68qi#Bht!-l`dP zb))4B(`+jg9sm6N59Y68Gkk(@9HG@!Vf((p@us7Y4%&(SmwL!6XdskPtpdJ1P?Jyl zmYF6EWnsZPG?m*}iBlhV^8NKs@vC~7)qeGT%(bpIU+J`I&rFadnp!64^;O2*8(;0C z4v_w>?R4xfsSiF6teL_t^1Dl}B_x22LRggrCZZ>qHoN5`FQ3(PpZKTQ`AhPP%i$=y zhKeCIcy{=+GU!9-?1FLoOhxrN5kOyALfrldQ9d&}DOF-f*D`wg8SlI0+`R4#8qoW+ zNa^1HW2lzP8$FK(l^DOddDZ;!Ec$h#P|1@SX%Rxc{?KSR+_9pf#;xMlMLn*>03r1) zNsymd1*}1BZ&ji7NP7tHdk6yEP|VD?ed=V3gg|~2icd%gZMyOQ;Ia8|KELd?QKfA0 z&QF;|EZ;@ql4?$0S2GC5+K9YdxoeKa(Z!{d$=qMcZX&xmHb$cn*Z5m&h<>eM$}=Nc zqKRt7Md>FL$>Fzk+4|LH>V9uT9GzOS&;|+X;m1G#| zZFl&hyp7vj>`*=&4+$R^(2i5-A#Sne)UPVB9#TIetYC6B+uZB0JA7z*_sco1xcxP7 z)v>g_A`|PtZ>Q6HtyD%q9YK~h=X=xihCo%qZHJdu5}G7KPnYjmX`qGiwD?(~_~4kn z$mLf@CIP+ZfI3v*k<+{+ZPKGXv+H_;fmQ;3#3FVIB2tWoQ%3}wzo2e8h7OH`qlY-Q zY)LC*7AoyGnFw{%)NX!gxVQGfU4E&cKMc4Sa&lJspgiCn`kbkWy6(KwDius!#O7>^ z|Miscct6qcu3?iy=#u55sNF=m%vdT9m2u4WF=H+;C3zmO2 z<=Hf9oUY$x9$3Hvyp$`G3V6^q>fAGAd@8eiI;b zJoUuZ4N?yBb79k?pmpL~Vl6DD$)+BeNLf}JO{ExOiyE-7<71l6&6dA}cTI_buWF4| z8g0Yrms2(}XphWbO|s2;Qq{-DMKh&5@=wYVKz8UsdaGW6MVmV18N068IRw;QR!wH zxF;?`YQ_w9_y|{O-_`}^jV?(hR&KH7=#;1;P9HcW7UUU2nL>&rlmrYRp5JbDvzW-z zu;#o>R;+T+!P$XV&*>xr|Mo`gdMx|K9iBdsRa147iG}&{mrPj@H?v@FiPdHoH`)Oy zIHy;kmmjgN0_25(W~1Y|mT87%os|Z+hXhQWssNH3W~~M|iD_xoX2m)*n{>!~N^wBE z-FwMuZI zmM?7KCo&3(_k9!uAuC1Rrq5q(@@pEq-Ks9V2IepcDtx-z{c74#7N7G)hbkB;rM8Vg zqZM9b0`ds$5{-;u;zLr}?K+Z8jqfG=b{oWZTi%)USlLscE%)gTf2P;46t?vz9m64( zOx6;yqb`rAOB&E{ZO$wlrr+-ZoloO%A#W296)Z&PFm0rX1b4-aZ3_5Er3jp49K&p>Z_ zKt#bqEkRgqq1=eE@s}h{I$S>+^f@Q1#c4NyX1RIMlXieF5)1GxSzak?B*?8!E~_Uv!y$1BEe0}@59*2; z1_3R-TKdB+h@z2>3Rf`KsHzr6nbNRuK?)_BQ3Szy)^t(!rQ8 zrO<^9O)m58ce^YR>gaanu;Vn=$}h`9@wEHUTk-r>QFbF{kG+q z7adkd09{wPqD2qQ>vxM7%`BlCIX1Er(2RRomxa4y9eT-n_qfBouPIkG1q1l*$Ii_| zWUssL6r-fnGFc4{eFdH`i4z6GPf@t@Sbo=68_#WZ%kG|?8c^C#+F|V+3yHt$AZhYB zeOqiWGZ?$SyZz+Mh+Hd^?g6)4@&70K`o8jK#jq%FD&f+4Xy+%N z8R7%Nv@Ip)*$cMIf!5ig5pkCVrv05zCOh9=95G6}l)DK5;y6puFzZN~4Ix2Ou_(%p zboB^=5pR>mG+=>?%da1hv4mHD^kIv#b=wFPww<#Fg+-`go^|439CKoGA>VY`2rZ;R zB^choq$B-7(c0Kog)u^Vnq zo2j{Z(NXS;IH|hA?8nDIuheG_)692uvD$l_;tE{puiYQTsW(n@V2*)H_us?m^o?DO z)U!{A-j!F282_^WseSI>wIb{PsR{;|85RRhMOxnx{)}R*H~nrn;scSx<7ht98Mo3{ z3Zu>v_hscNhUuoWL$bbqp_<5hlk+KB^_`;si=}0EJ)_Qgp5fWd;wEUnKv6PjqO_YM zXZ~}BY@+_}P@5Zt`zZx+7s?@@D1{>_7d!P_F45zTI!hl=pA${Z`fH)50>F(>b^Gy~ zip>G59Nn)z9~K5$8V|R5A?~?Zn|AXn4pcnXx$H!aV~Gv3q4lmNABPd{cDp)p6&h#E zK~gTXRYw!%-=y`#pM}k^erGH>p^jL4yu;UhX{lc6;J*E{lk*s6`XK7^naaFl{(^k6 z-$D{IRgyA~24ZK6|B5CbR%F3doUjN2PAvwA&(FCJfH??m2P$-KoJAJWXS> z5#KRN&LP1cm1_s^&a}6<7;k*M|McUC4p^HCni$kWTrDE!t4%fHA%V#poU2TfJSk6o zgk2hA6x@u&y8BI0i#42SJU?-~OctX_gW((#!+SAqu@D3V`ouZ9t$W^hxbs@n^&V8r z7HRPQ<1*g?_%g5|g74KTG*-qkL{o7Cb`s*bzms7{w|!l6yV5jU{8l(<9qq%PsG;3A zW+ZB1o@C4Y`yvW)H0vQZkw18is-&doBH*X$EqEFvL)0&JK{fUGBSDh`UY$=bxy^X< zrps|fG)T_EDg9x2_eq!Z;;Q=#Rj`~D-aI)F&uf|ek2m-NcUWg-UpE<>TUusDbnJYl zd!+)&Z3QaK0ddj0?i9d^QcN$S3;fS+oCawdP5q1W1E1|2okr}LBCfb)kTp_C$R+=AqGp6sofc<-5mHg|{hnB7^I#xVHe z>1oRcH z?dEJ*WPl}Gtvt)sZ6hx|v$U`t32p_`V_aAKzSw`b&P*UL z5wQ2{!~W#)UnhCuJQ+4TdlRgu9Q#CVz3&?^T`l9nW2?hfS@f&FG#A@E=36Iu!PXtE zH7b-mQ!eIyp>cT*UzPm#|1_0F)QwNF4t7uPI>I}=c`7@*a}Rv)lqN4TmyABsKMIP*hR7WHfr9b9F* zw1tUFS#i zZCbQF)UcwUe0Muhb z*DYn6E*nZSqC7%3FbM>c(loJl`0dAqJ*;ia|8&z*`K5`&Q+GDCRk5Je>O@D5X^=Rf z2&Vl?FqCJgS#LufIb_dAmewDqpoL&#P=Sp93$NaPyem0vVW)XkSOfzQv)8!RJewt< zKvi=5+>}h5I>IqainIzJDQLOtTo0xLpr{`r1yS0M&&U0^fg8;=ik}mlG2X?+^8T44 z=XsaA@k_EaV=IukAsYIh$n^zK4CID|N<9z>3;LQGj12BvaQ4}snc~l;x`bJhp$w2? z+RPGzP|LWlBEMcTL!F-$v<^=9Tq%+u`Cf#uKCe(4F>yqO@s(M^&yrb?tamy2n1)_{ zt`*<(`-68SI5vVh#1g@z{E8Hb5&v6l{BOEWl(VaevIlz1Uu6`>F$s|Zn5da=$v}X* zr&|xSe1@{Fp9MKLt>?5*V?SvYnL*(Xu7kYhEo0Chn%I}aw7^$ZDu z-qeH7?gIK0->y&$fFV(DA~sJ05MP7ch%68m*VRbP;psg^L~L@<`^_GtfRPFaxyXFb ze^XX%%wBkj5y$B$f$4|iIQAe%lvZEuKepa_%KY~^{y(+3eZ^{Rd^6YwMJWB|CjsQc zR=jmqW>Xo6CRFBGX?YtnSg(c?nT!AeW+u{4@jcF;;HRI2Y#mj=*dx#k(@!rgdFPB< zUK%I@@T~pv1*ITuL&(@tE(DsVb4b&wr`r)^VVa9FtDhUzF-cXyjf%{dtl9)Oj>+~2 zln=PfMJ}JJXy)9zn(UopaMi!GNLrfEI^`CMP|=WI(ci5ilO&B(IU)GytI8nr$(%sb zI1r~Ig+5ND`Y~7#1G!h+;NRpn4ah&yv{`vsUke(vo zZj>mY80L}VuMzYWFIgKestrzpEgiMV=jq4`g9s~I#<>@&f}Ii^I|0pt+ieRi_L(rn z3R7H4iUp*VZ54okvDe?Hba*KA;{u54n2=hVXiF`dX)1W4q7e)y$@yeFaBbRtVxK6` zT+F5lk@ExIX!DqXYP6Nszx~7>)-x{1Fnx?BDTD#+K{co5@9Xjtz=P)!>2E|~7|nW- zLclv$6X@~@pAtZTT~QS2tHiZ#v)ZcE-0An8d)SI=O;meOeK<#wWJj1 z(AGZI%`u$}+lFpLJ1-23xgz!T!NC?UUW6$JKN=a>pk&%CfSGugWN0?CfTf^qT_y=q zLjSZ{mb3x};ARKLFW@yOF(Yk&+BRt}H5g+6ahoKCnSuNm;?t^iaFYJ~R&g`oVb-W% zhoc12mTl-S6dpFU;qTPBa4SHBe9;sldMpVFj?G zcbEg7Y@|g1MWmIYo0L_HMZzID|2ta zRKG8#Vx8Adq79MX0f8FmYJAJF)soBWrC!AUK4T#Eps))&)TBfme-L&k@Bg}Djc*F8 zU=VCGY!DP*Q5`k_DQ@E4GgoqaOBKC29cf^JG?*L1ne}tp4{w7>58;)lrm;L;-sZC|LT%3ehmEO{C-RN<~zL zyy;8<47bG6g*pe(I0W3-y(ch(RzDd17HUBaZh%Hcy8L+s0HG z8VK+l#}-o1hY-)``NkOjl^p!b=Le2PC;eMFGxODVz4e}77AFwLe)ervg{Zajotz3I z4b^Pw8t13qt^Q21s14Zm?T`K|w8*~fN{l!a0)=G8L~zx=b}}cjjVn(^0tjgp;Vt9# zHNc@s6HjG-#eh~1wa_%4t|0;e9oV%re*y{*dh%ggX2@iwn{Qzg5w`3!k`<*+zTd4y z8?u&`B-s5SHse7EmN~c$4Bb(Mb$qJfck33vT+~a_guNc*B~`pBnFryu5k->OH2~23 zD>$e#0qA-l$@H4wyPEX6%9YzNyDo=7gflZ^L?uf+uJw>(fq$3(gl{&;g`#V*<)4gd zd(Lny_fPvET>q^zl`7y$eVJNW=nP|Nwawou1b76fyw7MYWEL;jb z+czp{LuR{A;mQR)AeXW1ZL>A$Ob7eSu3;Eg8Mm=OAYs@>YZF&<=k>p$U!5XqqN0mfw}2Yz)RB133n5%UDBt zM|9u^W#V9ToeDAM*pUBNEgQL5i z*+r9keRWDPi8~Ma)ZbkwfzLz!G{!L7Q4*-yGB_m}j@tXN$8l2DkJp+H^HSLagGF$9 zLQ=jw9=1a(eX=mRLck+{!By$g_5>EY{wQti9;i}pq;!&EkHGl*QSC35&JZ=LyRoG| zn(xWSmcX<4Xdy(ryC`DS+@DT9HV{&vIkYf_e}}%BCgx=l1^9T)n!n)b`bkFndDHF{ zjlumb88>bu_-I~p239u%C)OlqhsmN*ZE_HksjGlOs@mKc#J<57Z}~yo7(fhmAciO^ zYndAIxQ0DX*HAB4|G24LA!ZSy3~=MWvTYDYi7)-caq+nhsjba)Ari_&zfYEiFTA2BAu>#n2iu4f0UdnA0(NlzKxO8G4@qO_LBNc??TZu(SSR ze_t(OG^hWcve$}8c-*8KJ{uY6s~K|Meje0*TcpeR&#{@><$UWXgIq3TVU>+HM7}wp zCOt!xl@eyw)fnq);>{ju0r(J}3p;Jit{ZESJKu|9xz&_)i!p@4*{qaiM1a4>m;<3q6Zd;ZP_0lM?)PhKOT~vvUj4S8a8AYcfL7 z=kRKYdz2busMvX-hnENiRSeD5#OqxG0UAR-7b*y7(}DRZ1OD2Ck!TI3=zU-mzyZ=;mJ+3ql|{h~NVMcb zYAQ@L1`CD%DcnH;ARYSuuUr0qsABuSl)PzDpgj@lL`+ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/views/account_invoice_state_view.xml b/views/account_invoice_state_view.xml new file mode 100644 index 000000000..dbf628e23 --- /dev/null +++ b/views/account_invoice_state_view.xml @@ -0,0 +1,22 @@ + + + + + + + ncf manager account invoice view + account.invoice.cancel + + + + + + + + + + + + + + \ No newline at end of file diff --git a/views/account_invoice_view.xml b/views/account_invoice_view.xml new file mode 100644 index 000000000..ee18e3c60 --- /dev/null +++ b/views/account_invoice_view.xml @@ -0,0 +1,92 @@ + + + + + + ncf manager account invoice form + account.invoice + + + + +