def _validahacienda_pos(self, max_orders=10, no_partner=True): # cron pos_orders = self.env['pos.order'].search([('state', 'in', ('paid', 'done', 'invoiced')), '|', (no_partner, '=', True), '&', ('partner_id', '!=', False), ('partner_id.vat', '!=', False), ('tipo_documento', 'in', ('TE','FE','NC')), ('state_tributacion', '=', False)], order="date_order", limit=max_orders) total_orders = len(pos_orders) current_order = 0 _logger.info( 'MAB - Valida Hacienda - POS Orders to check: %s', total_orders) for doc in pos_orders: current_order += 1 _logger.info('MAB - Valida Hacienda - POS Order: "%s" - %s / %s', doc.number_electronic, current_order, total_orders) docName = doc.number_electronic if not docName or not docName.isdigit() or doc.company_id.frm_ws_ambiente == 'disabled': _logger.error( 'MAB - Valida Hacienda - skipped Invoice %s', docName) doc.state_tributacion = 'no_aplica' continue now_utc = datetime.datetime.now(pytz.timezone('UTC')) now_cr = now_utc.astimezone(pytz.timezone('America/Costa_Rica')) dia = docName[3:5]#'%02d' % now_cr.day, mes = docName[5:7]#'%02d' % now_cr.month, anno = docName[7:9]#str(now_cr.year)[2:4], date_cr = now_cr.strftime("20"+anno+"-"+mes+"-"+dia+"T%H:%M:%S-06:00") doc.name = doc.number_electronic[21:41] if not doc.xml_comprobante: numero_documento_referencia = False fecha_emision_referencia = False codigo_referencia = False razon_referencia = False invoice_comments = False tipo_documento_referencia = False if not doc.pos_order_id: #.number_electronic: if doc.amount_total < 0: doc.state_tributacion = 'error' _logger.error( 'MAB - Error documento %s tiene monto negativo pero no tiene documento referencia', doc.number_electronic) continue else: if doc.amount_total >= 0: _logger.error( 'MAB - Valida Hacienda - skipped Invoice %s', docName) doc.state_tributacion = 'no_aplica' continue doc.tipo_documento = 'ND' razon_referencia = 'Reemplaza Factura' else: doc.tipo_documento = 'NC' numero_documento_referencia = doc.pos_order_id.number_electronic codigo_referencia = doc.reference_code_id.code razon_referencia = 'nota credito' tipo_documento_referencia = doc.pos_order_id.number_electronic[29:31] numero_documento_referencia = doc.pos_order_id.number_electronic fecha_emision_referencia = doc.pos_order_id.date_issuance codigo_referencia = doc.reference_code_id.code sale_conditions = '01' #Contado !! doc.sale_conditions_id.sequence, currency_rate = 1 # 1 / doc.currency_id.rate # Generamos las líneas de la factura lines = dict() otros_cargos = dict() otros_cargos_id = 0 line_number = 0 total_servicio_gravado = 0.0 total_servicio_exento = 0.0 total_servicio_exonerado = 0.0 total_mercaderia_gravado = 0.0 total_mercaderia_exento = 0.0 total_mercaderia_exonerado = 0.0 total_descuento = 0.0 total_impuestos = 0.0 base_subtotal = 0.0 total_otros_cargos = 0.0 total_iva_devuelto = 0.0 for line in doc.lines: line_number += 1 price = line.price_unit * (1 - line.discount / 100.0) qty = abs(line.qty) if not qty: continue fpos = line.order_id.fiscal_position_id tax_ids = fpos.map_tax( line.tax_ids, line.product_id, line.order_id.partner_id) if fpos else line.tax_ids line_taxes = tax_ids.compute_all( price, line.order_id.pricelist_id.currency_id, 1, product=line.product_id, partner=line.order_id.partner_id) price_unit = round( line_taxes['total_excluded'] / (1 - line.discount / 100.0), 5) base_line = abs(round(price_unit * qty, 5)) subtotal_line = abs( round(price_unit * qty * (1 - line.discount / 100.0), 5)) dline = { "cantidad": qty, "unidadMedida": line.product_id and line.product_id.uom_id.code or 'Sp', "detalle": escape(line.product_id.name[:159]), "precioUnitario": price_unit, "montoTotal": base_line, "subtotal": subtotal_line, } if line.discount: descuento = abs(round(base_line - subtotal_line, 5)) total_descuento += descuento dline["montoDescuento"] = descuento dline["naturalezaDescuento"] = 'Descuento Comercial' taxes = dict() _line_tax = 0.0 if tax_ids: tax_index = 0 taxes_lookup = {} for i in tax_ids: taxes_lookup[i.id] = { 'tax_code': i.tax_code, 'tarifa': i.amount, 'iva_tax_desc': i.iva_tax_desc, 'iva_tax_code': i.iva_tax_code} for i in line_taxes['taxes']: if taxes_lookup[i['id']]['tax_code'] == 'service': total_otros_cargos += round(abs(i['amount'] * qty), 5) elif taxes_lookup[i['id']]['tax_code'] != '00': tax_index += 1 tax_amount = round(abs(i['amount'] * qty), 5) _line_tax += tax_amount taxes[tax_index] = { 'codigo': taxes_lookup[i['id']]['tax_code'], 'tarifa': taxes_lookup[i['id']]['tarifa'], 'monto': tax_amount, 'iva_tax_desc': taxes_lookup[i['id']]['iva_tax_desc'], 'iva_tax_code': taxes_lookup[i['id']]['iva_tax_code'], } dline["impuesto"] = taxes dline["impuestoNeto"] = _line_tax if line.product_id and line.product_id.type == 'service': if taxes: total_servicio_gravado += base_line total_impuestos += _line_tax else: total_servicio_exento += base_line else: if taxes: total_mercaderia_gravado += base_line total_impuestos += _line_tax else: total_mercaderia_exento += base_line base_subtotal += subtotal_line dline["montoTotalLinea"] = round(subtotal_line + _line_tax, 5) lines[line_number] = dline if total_otros_cargos: total_otros_cargos = round( total_otros_cargos, 5) otros_cargos_id = 1 otros_cargos[otros_cargos_id]= { 'TipoDocumento': '06', 'Detalle': escape('Servicio salon 10%'), 'MontoCargo': total_otros_cargos } doc.date_issuance = date_cr invoice_comments = '' doc.economic_activity_id = doc.company_id.activity_id xml_string_builder = api_facturae.gen_xml_v43( doc, sale_conditions, round(total_servicio_gravado, 5), round(total_servicio_exento, 5), total_servicio_exonerado, round(total_mercaderia_gravado, 5), round(total_mercaderia_exento, 5), total_mercaderia_exonerado, total_otros_cargos, total_iva_devuelto, base_subtotal, total_impuestos, total_descuento, json.dumps(lines, ensure_ascii=False), otros_cargos, currency_rate, invoice_comments, tipo_documento_referencia, numero_documento_referencia, fecha_emision_referencia, codigo_referencia, razon_referencia) xml_to_sign = str(xml_string_builder) xml_firmado = api_facturae.sign_xml( doc.company_id.signature, doc.company_id.frm_pin, xml_to_sign) doc.fname_xml_comprobante = doc.tipo_documento + '_' + docName + '.xml' doc.xml_comprobante = base64.encodestring(xml_firmado) _logger.info('MAB - SIGNED XML:%s', doc.fname_xml_comprobante) else: xml_firmado = doc.xml_comprobante # get token token_m_h = api_facturae.get_token_hacienda( doc, doc.company_id.frm_ws_ambiente) response_json = api_facturae.send_xml_fe(doc, token_m_h, date_cr, xml_firmado, doc.company_id.frm_ws_ambiente) response_status = response_json.get('status') response_text = response_json.get('text') if 200 <= response_status <= 299: doc.state_tributacion = 'procesando' else: if response_text.find('ya fue recibido anteriormente') != -1: doc.state_tributacion = 'procesando' doc.message_post( subject='Error', body='Ya recibido anteriormente, se pasa a consultar') elif doc.error_count > 10: doc.message_post(subject='Error', body=response_text) doc.state_tributacion = 'error' _logger.error('MAB - Invoice: %s Status: %s Error sending XML: %s', doc.name, response_status, response_text) else: doc.error_count += 1 doc.state_tributacion = 'procesando' doc.message_post(subject='Error', body=response_text) _logger.error('MAB - Invoice: %s Status: %s Error sending XML: %s', doc.name, response_status, response_text) _logger.info('MAB 014 - Valida Hacienda POS- Finalizado Exitosamente')
def _validahacienda_pos(self, max_orders=10): # cron lock.acquire() try: pos_orders = self.env['pos.order'].search( [('state', 'in', ('paid', 'done', 'invoiced')), ('number_electronic', '=', False), ('date_order', '>=', '2018-09-01'), ('state_tributacion', '=', False)], order="date_order", limit=max_orders) total_orders = len(pos_orders) current_order = 0 _logger.error('MAB - Valida Hacienda - POS Orders to check: %s', total_orders) for doc in pos_orders: current_order += 1 _logger.error('MAB - Valida Hacienda - POS Order %s / %s', current_order, total_orders) docName = doc.name if doc.company_id.frm_ws_ambiente != 'disabled' and docName.isdigit( ): url = doc.company_id.frm_callback_url if doc.amount_total >= 0: tipo_documento = 'FE' tipo_documento_referencia = '' numero_documento_referencia = '' fecha_emision_referencia = '' codigo_referencia = '' razon_referencia = '' else: tipo_documento = 'NC' tipo_documento_referencia = 'FE' numero_documento_referencia = doc.pos_order_id.number_electronic fecha_emision_referencia = doc.pos_order_id.date_issuance codigo_referencia = doc.reference_code_id.code razon_referencia = 'nota credito' # FacturaReferencia = '' ***************** now_utc = datetime.datetime.now(pytz.timezone('UTC')) now_cr = now_utc.astimezone( pytz.timezone('America/Costa_Rica')) dia = docName[3:5] # '%02d' % now_cr.day, mes = docName[5:7] # '%02d' % now_cr.month, anno = docName[7:9] # str(now_cr.year)[2:4], # date_cr = now_cr.strftime("%Y-%m-%dT%H:%M:%S-06:00") date_cr = now_cr.strftime("20" + anno + "-" + mes + "-" + dia + "T%H:%M:%S-06:00") # date_cr = now_cr.strftime("2018-09-01T07:25:32-06:00") codigo_seguridad = docName[ -8:] # ,doc.company_id.security_code, if not doc.statement_ids[ 0].statement_id.journal_id.payment_method_id: _logger.error( 'MAB 001 - codigo seguridad : %s -- Pedido: %s Metodo de pago de diario no definido, utilizando efectivo', codigo_seguridad, docName) medio_pago = doc.statement_ids[ 0].statement_id.journal_id.payment_method_id and doc.statement_ids[ 0].statement_id.journal_id.payment_method_id.sequence or '01' sale_conditions = '01' # Contado !! doc.sale_conditions_id.sequence, currency_rate = 1 # 1 / doc.currency_id.rate lines = [] numero = 0 vat = doc.partner_id.vat if doc.partner_id and doc.partner_id.vat: # and doc.partner_id.email: if not doc.partner_id.identification_id: if len(doc.partner_id.vat) == 9: # cedula fisica id_code = '01' elif len(doc.partner_id.vat ) == 10: # cedula juridica id_code = '02' elif len(doc.partner_id.vat) == 11 or len( doc.partner_id.vat) == 12: # dimex id_code = '03' else: id_code = '05' else: id_code = doc.partner_id.identification_id.code if id_code == '05': receptor_identificacion = { 'tipo': False, 'numero': False, } receptor_identificacion_extranjero = doc.partner_id.vat else: receptor_identificacion = { 'tipo': id_code, 'numero': doc.partner_id.vat, } receptor_identificacion_extranjero = '' receptor = { 'nombre': doc.partner_id.name[:80], 'identificacion': receptor_identificacion, 'IdentificacionExtranjero': receptor_identificacion_extranjero, } else: receptor = '' # Generamos las líneas de la factura lines = dict() line_number = 0 total_servicio_gravado = 0.0 total_servicio_exento = 0.0 total_mercaderia_gravado = 0.0 total_mercaderia_exento = 0.0 total_descuento = 0.0 total_impuestos = 0.0 base_subtotal = 0.0 for line in doc.lines: # impuestos_acumulados = 0.0 numero += 1 price = line.price_unit * (1 - line.discount / 100.0) qty = abs(line.qty) if not qty: continue fpos = line.order_id.fiscal_position_id tax_ids = fpos.map_tax( line.tax_ids, line.product_id, line.order_id.partner_id) if fpos else line.tax_ids line_taxes = tax_ids.compute_all( price, line.order_id.pricelist_id.currency_id, 1, product=line.product_id, partner=line.order_id.partner_id) price_unit = round(line_taxes['total_excluded'] / (1 - line.discount / 100.0), 5) # ajustar para IVI base_line = abs(round(price_unit * qty, 5)) subtotal_line = abs( round( price_unit * qty * (1 - line.discount / 100.0), 5)) dline = { "cantidad": qty, "unidadMedida": line.product_id and line.product_id.uom_id.code or 'Sp', "detalle": line.name[:159], "precioUnitario": price_unit, "montoTotal": base_line, "subtotal": subtotal_line, } if line.discount: descuento = abs(base_line - subtotal_line) total_descuento += descuento line["montoDescuento"] = descuento line["naturalezaDescuento"] = 'Descuento Comercial' # Se generan los impuestos taxes = dict() impuesto_linea = 0.0 if tax_ids: tax_index = 0 taxes_lookup = {} for i in tax_ids: taxes_lookup[i.id] = { 'tax_code': i.tax_code, 'tarifa': i.amount } for i in line_taxes['taxes']: if taxes_lookup[i['id']]['tax_code'] != '00': tax_index += 1 tax_amount = abs( round(i['amount'], 5) * qty) impuesto_linea += tax_amount taxes[tax_index] = { 'codigo': taxes_lookup[i['id']]['tax_code'], 'tarifa': taxes_lookup[i['id']]['tarifa'], 'monto': tax_amount, } dline["impuesto"] = taxes # Si no hay product_id se asume como mercaderia if line.product_id and line.product_id.type == 'service': if taxes: total_servicio_gravado += base_line total_impuestos += impuesto_linea else: total_servicio_exento += base_line else: if taxes: total_mercaderia_gravado += base_line total_impuestos += impuesto_linea else: total_mercaderia_exento += base_line base_subtotal += subtotal_line dline[ "montoTotalLinea"] = subtotal_line + impuesto_linea lines[line_number] = dline consecutivo = docName[21:41] doc.number_electronic = docName response_json = functions.make_xml_invoice( doc, 'TE', consecutivo, date_cr, sale_conditions, medio_pago, total_servicio_gravado, total_servicio_exento, total_mercaderia_gravado, total_mercaderia_exento, base_subtotal, total_impuestos, total_descuento, json.dumps(lines, ensure_ascii=False), tipo_documento_referencia, numero_documento_referencia, fecha_emision_referencia, codigo_referencia, razon_referencia, url, currency_rate) _logger.error('MAB - unsigned JSON DATA:%s', response_json) xml = response_json.get('resp').get('xml') response_json = functions.sign_xml(doc, tipo_documento, url, xml) xml_firmado = response_json.get('resp').get('xmlFirmado') _logger.error('MAB - SIGNED XML:%s', xml_firmado) # get token # url param added by @jbarboza token_m_h = functions.get_token_hacienda( doc, doc.company_id.frm_ws_ambiente) _logger.error('MAB 002 - enviando documento') response_json = functions.send_xml_fe( doc, token_m_h, date_cr, xml_firmado, doc.company_id.frm_ws_ambiente) _logger.error('MAB 003 - respuesta recibida') if response_json.get('resp').get('Status') == 202: # functions.consulta_documentos(self, doc, 'TE', doc.company_id.frm_ws_ambiente, token_m_h, url, date_cr, xml_firmado) doc.state_tributacion = 'procesando' doc.date_issuance = date_cr doc.fname_xml_comprobante = 'comprobante_' + doc.number_electronic + '.xml' doc.xml_comprobante = xml_firmado else: _logger.error( 'No se pudo Crear la factura electrónica: \n' + str(response_json.get('resp').get('text'))) number_electronic = '-1--' + docName + 'text :' + str( response_json.get('resp').get('text')) else: _logger.error( 'MAB 013 - Pos Order:%s skipped. FE disabled', doc.name) _logger.error( 'MAB 014 - Valida Hacienda POS- Finalizado Exitosamente') finally: lock.release()
def _consultahacienda_pos(self, max_orders=10): # cron pos_orders = self.env['pos.order'].search([('state', 'in', ('paid', 'done', 'invoiced')), ('number_electronic', '!=', False), ('state_tributacion', 'in', ('recibido', 'procesando'))], limit=max_orders) total_orders = len(pos_orders) current_order = 0 _logger.info( 'MAB - Consulta Hacienda - POS Orders to check: %s', total_orders) for doc in pos_orders: current_order += 1 _logger.info( 'MAB - Consulta Hacienda - POS Order %s / %s', current_order, total_orders) token_m_h = api_facturae.get_token_hacienda( doc, doc.company_id.frm_ws_ambiente) if doc.number_electronic and len(doc.number_electronic) == 50: response_json = api_facturae.consulta_clave( doc.number_electronic, token_m_h, doc.company_id.frm_ws_ambiente) status = response_json['status'] if status == 200: estado_m_h = response_json.get('ind-estado') elif status == 400: estado_m_h = response_json.get('ind-estado') _logger.error( 'MAB - Error: %s Documento:%s no encontrado en Hacienda', estado_m_h, doc.number_electronic) else: _logger.error( 'MAB - Error inesperado en Consulta Hacienda - Abortando') return if estado_m_h == 'aceptado': doc.state_tributacion = estado_m_h doc.fname_xml_respuesta_tributacion = 'AHC_' + doc.number_electronic + '.xml' doc.xml_respuesta_tributacion = response_json.get( 'respuesta-xml') if doc.partner_id and doc.partner_id.email: email_template = self.env.ref( 'cr_electronic_invoice_pos.email_template_pos_invoice', False) attachment = self.env['ir.attachment'].search( [('res_model', '=', 'pos.order'), ('res_id', '=', doc.id), ('res_field', '=', 'xml_comprobante')], limit=1) attachment.name = doc.fname_xml_comprobante attachment.datas_fname = doc.fname_xml_comprobante attachment_resp = self.env['ir.attachment'].search( [('res_model', '=', 'pos.order'), ('res_id', '=', doc.id), ('res_field', '=', 'xml_respuesta_tributacion')], limit=1) attachment_resp.name = doc.fname_xml_respuesta_tributacion attachment_resp.datas_fname = doc.fname_xml_respuesta_tributacion email_template.attachment_ids = [ (6, 0, [attachment.id, attachment_resp.id])] email_template.with_context(type='binary', default_type='binary').send_mail(doc.id, raise_exception=False, force_send=True) # default_type='binary' email_template.attachment_ids = [(5)] doc.state_email = 'sent' else: doc.state_email = 'no_email' _logger.info('MAB - Email no enviado - Cliente no definido') elif estado_m_h in ('firma_invalida'): if doc.error_count > 10: doc.state_tributacion = estado_m_h doc.fname_xml_respuesta_tributacion = 'AHC_' + doc.number_electronic + '.xml' doc.xml_respuesta_tributacion = response_json.get( 'respuesta-xml') doc.state_email = 'fe_error' _logger.error('MAB - Email no enviado - Factura rechazada') else: doc.error_count += 1 doc.state_tributacion = 'procesando' elif estado_m_h in ('rechazado', 'rejected'): doc.state_tributacion = estado_m_h doc.fname_xml_respuesta_tributacion = 'AHC_' + doc.number_electronic + '.xml' doc.xml_respuesta_tributacion = response_json.get( 'respuesta-xml') doc.state_email = 'fe_error' _logger.error('MAB - Email no enviado - Factura rechazada') elif estado_m_h == 'error': doc.state_tributacion = estado_m_h doc.state_email = 'fe_error' else: if doc.error_count > 10: doc.state_tributacion = 'error' elif doc.error_count < 4: doc.error_count += 1 doc.state_tributacion = 'procesando' else: doc.error_count += 1 doc.state_tributacion = '' _logger.error( 'MAB - Consulta Hacienda - POS Order no encontrada: %s', doc.number_electronic) else: doc.state_tributacion = 'error' _logger.error( 'MAB - POS Order %s - x Number Electronic: %s formato incorrecto', doc.name, doc.number_electronic) _logger.info('MAB - Consulta Hacienda POS - Finalizad Exitosamente')
def _consultahacienda_pos(self, max_orders=10): # cron pos_orders = self.env['pos.order'].search( [('state', 'in', ('paid', 'done', 'invoiced')), ('date_order', '>=', '2018-09-01'), ('number_electronic', '!=', False), ('state_tributacion', 'in', ('recibido', 'procesando'))], limit=max_orders) total_orders = len(pos_orders) current_order = 0 _logger.error('MAB - Consulta Hacienda - POS Orders to check: %s', total_orders) for doc in pos_orders: current_order += 1 _logger.error('MAB - Consulta Hacienda - POS Order %s / %s', current_order, total_orders) url = doc.company_id.frm_callback_url token_m_h = functions.get_token_hacienda( doc, doc.company_id.frm_ws_ambiente) if doc.number_electronic and len(doc.number_electronic) == 50: headers = {} payload = {} payload['w'] = 'consultar' payload['r'] = 'consultarCom' payload['client_id'] = doc.company_id.frm_ws_ambiente payload['token'] = token_m_h payload['clave'] = doc.number_electronic response = requests.request("POST", url, data=payload, headers=headers) responsejson = response.json() estado_m_h = responsejson.get('resp').get('ind-estado') if estado_m_h == 'aceptado': doc.state_tributacion = estado_m_h doc.fname_xml_respuesta_tributacion = 'AHC_' + doc.number_electronic + '.xml' doc.xml_respuesta_tributacion = responsejson.get( 'resp').get('respuesta-xml') if doc.partner_id and doc.partner_id.email and not doc.partner_id.opt_out: # email_template = self.env.ref('account.email_template_edi_invoice', False) email_template = self.env.ref( 'cr_electronic_invoice_pos.email_template_pos_invoice', False) attachment = self.env['ir.attachment'].search( [('res_model', '=', 'pos.order'), ('res_id', '=', doc.id), ('res_field', '=', 'xml_comprobante')], limit=1) attachment.name = doc.fname_xml_comprobante attachment.datas_fname = doc.fname_xml_comprobante attachment_resp = self.env['ir.attachment'].search( [('res_model', '=', 'pos.order'), ('res_id', '=', doc.id), ('res_field', '=', 'xml_respuesta_tributacion')], limit=1) attachment_resp.name = doc.fname_xml_respuesta_tributacion attachment_resp.datas_fname = doc.fname_xml_respuesta_tributacion email_template.attachment_ids = [ (6, 0, [attachment.id, attachment_resp.id]) ] email_template.with_context( type='binary', default_type='binary').send_mail( doc.id, raise_exception=False, force_send=True) # default_type='binary' # email_template.attachment_ids = [(3, attachment.id)] # email_template.attachment_ids = [(4, attachment_resp.id)] email_template.attachment_ids = [(5)] doc.state_email = 'sent' else: doc.state_email = 'no_email' _logger.info('email no enviado - cliente no definido') elif estado_m_h in ('rechazado', 'rejected'): doc.state_tributacion = estado_m_h doc.fname_xml_respuesta_tributacion = 'respuesta_' + doc.number_electronic + '.xml' doc.xml_respuesta_tributacion = responsejson.get( 'resp').get('respuesta-xml') doc.state_email = 'fe_error' _logger.info('email no enviado - factura rechazada') elif estado_m_h == 'error': doc.state_tributacion = estado_m_h doc.state_email = 'fe_error' else: doc.state_tributacion = 'no_encontrado' _logger.error( 'MAB - Consulta Hacienda - POS Order not found: %s', doc.number_electronic) else: doc.state_tributacion = 'error' _logger.error( 'MAB - POS Order %s - x Number Electronic: %s formato incorrecto', doc.name, doc.number_electronic) _logger.error('MAB - Consulta Hacienda POS- Finalizad Exitosamente')