diff --git a/febraban/cnab240/itau/sisdeb/__init__.py b/febraban/cnab240/itau/sisdeb/__init__.py new file mode 100644 index 0000000..f3658cb --- /dev/null +++ b/febraban/cnab240/itau/sisdeb/__init__.py @@ -0,0 +1,3 @@ +from .file.file import File +from .result.parser import PaymentParser +from .payment.debit import Debit diff --git a/febraban/cnab240/itau/sisdeb/file/__init__.py b/febraban/cnab240/itau/sisdeb/file/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/febraban/cnab240/itau/sisdeb/file/file.py b/febraban/cnab240/itau/sisdeb/file/file.py new file mode 100644 index 0000000..694cb46 --- /dev/null +++ b/febraban/cnab240/itau/sisdeb/file/file.py @@ -0,0 +1,48 @@ +from datetime import datetime +from .header import Header +from .trailer import Trailer +from ....libs.fileUtils import FileUtils + + +class File: + + def __init__(self): + self.header = Header() + self.lots = [] + self.trailer = Trailer() + + def addLot(self, lot): + lot.setLotNumber(len(self.lots) + 1) + self.lots.append(lot) + + def toString(self, currentDatetime=None): + lotsToString = "\r\n".join([lot.toString() for lot in self.lots]) + self.header.setGeneratedFileDate(currentDatetime or datetime.now()) + self.trailer.setNumberOfLotsAndRegisters( + num=len(self.lots), + sum=2 + self._countRegistersInLots() + ) + return "%s\r\n%s\r\n%s\r\n" % ( + self.header.content, + lotsToString, + self.trailer.content + ) + + def setSender(self, sender): + self.header.setSender(sender) + self.header.setSenderBank(sender.bank) + self.trailer.setSenderBank(sender.bank) + + def setBankAgreementCode(self, code): + self.header.setBankAgreementCode(code) + + def output(self, fileName, path="/../", content=None, currentDatetime=None): + file = FileUtils.create(name=fileName, path=path) + file.write(self.toString(currentDatetime or datetime.now()) if not content else content) + file.close() + + def _countRegistersInLots(self): + count = 0 + for lot in self.lots: + count += lot.count + return count diff --git a/febraban/cnab240/itau/sisdeb/file/header.py b/febraban/cnab240/itau/sisdeb/file/header.py new file mode 100644 index 0000000..b3b4a07 --- /dev/null +++ b/febraban/cnab240/itau/sisdeb/file/header.py @@ -0,0 +1,53 @@ +# coding: utf-8 + +from ....row import Row +from ....characterType import numeric, alphaNumeric + + +class Header: + + def __init__(self): + self.content = " " * 240 + self.defaultValues() + + def defaultValues(self): + structs = [ + ( 3, 8, 5, numeric, "0"), # Tipo de registro + ( 58, 65, 7, numeric, "0000000"), + ( 102, 132, 30, alphaNumeric, "BANCO ITAU"), + ( 142, 143, 1, numeric, "1"), # 1 - Remessa / 2 - Retorno + ( 157, 163, 6, numeric, "0"), # TODO: Número sequencial do arquivo + ( 163, 166, 3, numeric, "040"), # NR. DA VERSÃO DO LAYOUT + ( 166, 171, 5, numeric, "0"), + ] + self.content = Row.setStructs(structs=structs, content=self.content) + + def setGeneratedFileDate(self, datetime): + structs = [ + (143, 151, 8, numeric, datetime.strftime("%d%m%Y")), # Dia que o arquivo foi gerado + (151, 157, 6, numeric, datetime.strftime("%H%M%S")), # Horario que o arquivo foi gerado + ] + self.content = Row.setStructs(structs=structs, content=self.content) + + def setSender(self, user): + structs = [ + (17, 18, 1, numeric, "1" if len(user.identifier) == 11 else "2"), + (18, 32, 14, numeric, user.identifier), + (72, 102, 30, alphaNumeric, user.name) + ] + self.content = Row.setStructs(structs=structs, content=self.content) + + def setSenderBank(self, bank): + structs = [ + ( 0, 3, 3, numeric, bank.bankId), + (52, 57, 5, numeric, bank.branchCode), + (65, 70, 5, numeric, bank.accountNumber), + (71, 72, 1, numeric, bank.accountVerifier), + ] + self.content = Row.setStructs(structs=structs, content=self.content) + + def setBankAgreementCode(self, code): + structs = [ + (32, 45, 13, alphaNumeric, code), + ] + self.content = Row.setStructs(structs=structs, content=self.content) diff --git a/febraban/cnab240/itau/sisdeb/file/headerLot.py b/febraban/cnab240/itau/sisdeb/file/headerLot.py new file mode 100644 index 0000000..2f6e490 --- /dev/null +++ b/febraban/cnab240/itau/sisdeb/file/headerLot.py @@ -0,0 +1,66 @@ +# coding: utf-8 + +from ....row import Row +from ....characterType import numeric, alphaNumeric + + +class HeaderLot: + + def __init__(self): + self.content = " " * 240 + self.defaultValues() + + def defaultValues(self): + structs = [ + ( 3, 7, 4, numeric, "1"), + ( 7, 8, 1, numeric, "1"), + ( 8, 9, 1, alphaNumeric, "D"), + ( 13, 16, 3, numeric, "030"), + ( 58, 65, 7, numeric, "0000000"), + ] + self.content = Row.setStructs(structs=structs, content=self.content) + + def setSender(self, user): + structs = [ + (17, 18, 1, numeric, "1" if len(user.identifier) == 11 else "2"), + (18, 32, 14, numeric, user.identifier), + (72, 102, 30, alphaNumeric, user.name) + ] + self.content = Row.setStructs(structs=structs, content=self.content) + + def setSenderBank(self, bank): + structs = [ + ( 0, 3, 3, numeric, bank.bankId), + (52, 57, 5, numeric, bank.branchCode), + (65, 70, 5, numeric, bank.accountNumber), + (71, 72, 1, numeric, bank.accountVerifier), + ] + self.content = Row.setStructs(structs=structs, content=self.content) + + def setSenderAddress(self, address): + structs = [ + (142, 192, 50, alphaNumeric, "%s %s %s" % (address.streetLine1, address.streetLine2, address.district)), + (192, 212, 20, alphaNumeric, address.city), + (212, 220, 8, numeric, address.zipCode), + (220, 222, 2, alphaNumeric, address.stateCode), + ] + self.content = Row.setStructs(structs=structs, content=self.content) + + def setPositionInLot(self, index): + structs = [ + (3, 7, 4, numeric, index) + ] + self.content = Row.setStructs(structs=structs, content=self.content) + + def setInfo(self, kind, method): + structs = [ + ( 9, 11, 2, numeric, kind), + (11, 13, 2, numeric, method) + ] + self.content = Row.setStructs(structs=structs, content=self.content) + + def setBankAgreementCode(self, code): + structs = [ + (32, 45, 13, alphaNumeric, code), + ] + self.content = Row.setStructs(structs=structs, content=self.content) \ No newline at end of file diff --git a/febraban/cnab240/itau/sisdeb/file/lot.py b/febraban/cnab240/itau/sisdeb/file/lot.py new file mode 100644 index 0000000..4a4fca2 --- /dev/null +++ b/febraban/cnab240/itau/sisdeb/file/lot.py @@ -0,0 +1,103 @@ +from .headerLot import HeaderLot +from .trailerLot import TrailerLot +from ....itau.sispag import Transfer, ChargePayment, BarCodePayment, NonBarCodePayment +from ....libs.paymentKind import PaymentKind +from ....libs.paymentMethod import PaymentMethod + + +class Lot: + + def __init__(self): + self.headerLot = HeaderLot() + self.registers = [] + self.trailerLot = TrailerLot() + self.kind = "" + self.method = "" + self.amount = 0 + self.otherAmount = 0 + self.additionAmount = 0 + self.totalAmount = 0 + self.index = 1 + self.count = 0 + + def add(self, register): + register.setPositionInLot(index=self.index) + self.registers.append(register) + self.amount += register.amountInCents() + if self._isNonBarCodeTax(): + self.otherAmount += register.otherAmountInCents() + self.additionAmount += register.additionAmountInCents() + self.totalAmount += register.totalAmountInCents() + self.index += 1 + + def setLotNumber(self, index): + self.headerLot.setPositionInLot(index) + self.trailerLot.setPositionInLot(index) + for register in self.registers: + register.setLot(index) + + def setSender(self, sender): + self.headerLot.setSender(sender) + self.headerLot.setSenderBank(sender.bank) + self.headerLot.setSenderAddress(sender.address) + self.trailerLot.setSenderBank(sender.bank) + + def setBankAgreementCode(self, code): + self.headerLot.setBankAgreementCode(code) + + def setHeaderLotType(self, kind=PaymentKind.vendor, method=PaymentMethod.tedOther): + """ + Trasfers: + kind: String - Kind of payment - 20 Fornecedores, read: NOTES 4 + method: String - Payment method - 41 TED Outro titular, 43 TED Mesmo titular, 01 ITAU account. read: NOTES 5 + + Charge-payments: + kind: String - Kind of payment - 98 Diversos, read: NOTES 4 + method: String - Payment method - 30 Pagamento Boleto Itau, 31 Pagamento Boleto outros Bancos. read: NOTES 5 + + Utilities: + kind: String - Kind of payment - 98 Diversos, read: NOTES 4 + method: String - Payment method - 13 Concessionarias. read: NOTES 5 + + Tax-payments: + kind: String - Kind of payment - 22 Tributos, read: NOTES 4 + method: String - Payment method - 91 GNRE e Tributos com Codigo de Barras, + 19 IPTU/ISS/Outros Tributos Municipais. read: NOTES 5, + 16 DARF (No barcode) + + """ + self.kind = kind + self.method = method + self.headerLot.setInfo(kind, method) + + def toString(self): + self.count = (2 + self._count(Transfer) + 2 * self._count(ChargePayment) + + self._count(BarCodePayment) + self._count(NonBarCodePayment)) + self.trailerLot.setLotNumberOfRegisters( + num=self.count + ) + + if self._isNonBarCodeTax(): + self.trailerLot.setSumOfValuesNonBarCodeTax( + sum=self.amount, + otherSum=self.otherAmount, + additionSum=self.additionAmount, + totalSum=self.totalAmount, + ) + else: + self.trailerLot.setSumOfValues(sum=self.amount) + + registersToString = "\r\n".join([register.toString() for register in self.registers]) + return "%s\r\n%s\r\n%s" % ( + self.headerLot.content, + registersToString, + self.trailerLot.content, + ) + + def _count(self, cls): + return len([register for register in self.registers if isinstance(register, cls)]) + + def _isNonBarCodeTax(self): + return self.kind == PaymentKind.tribute and self.method in PaymentMethod.nonBarcodeTaxes() + + diff --git a/febraban/cnab240/itau/sisdeb/file/trailer.py b/febraban/cnab240/itau/sisdeb/file/trailer.py new file mode 100644 index 0000000..591be1c --- /dev/null +++ b/febraban/cnab240/itau/sisdeb/file/trailer.py @@ -0,0 +1,30 @@ +# coding: utf-8 + +from ....row import Row +from ....characterType import numeric + + +class Trailer: + + def __init__(self): + self.content = " " * 240 + self.defaultValues() + + def defaultValues(self): + structs = [ + ( 3, 8, 5, numeric, "99999"), + ] + self.content = Row.setStructs(structs=structs, content=self.content) + + def setNumberOfLotsAndRegisters(self, sum, num): + structs = [ + (17, 23, 6, numeric, num), + (23, 29, 6, numeric, sum), + ] + self.content = Row.setStructs(structs=structs, content=self.content) + + def setSenderBank(self, bank): + structs = [ + (0, 3, 3, numeric, bank.bankId), # Código do banco debitado + ] + self.content = Row.setStructs(structs=structs, content=self.content) diff --git a/febraban/cnab240/itau/sisdeb/file/trailerLot.py b/febraban/cnab240/itau/sisdeb/file/trailerLot.py new file mode 100644 index 0000000..7ba3038 --- /dev/null +++ b/febraban/cnab240/itau/sisdeb/file/trailerLot.py @@ -0,0 +1,52 @@ +# coding: utf-8 + +from ....row import Row +from ....characterType import numeric + + +class TrailerLot: + + def __init__(self): + self.content = " " * 240 + self.defaultValues() + + def defaultValues(self): + structs = [ + ( 3, 7, 4, numeric, "1"), + ( 7, 8, 1, numeric, "5"), + (41, 59, 18, numeric, "0"), + ] + self.content = Row.setStructs(structs=structs, content=self.content) + + def setLotNumberOfRegisters(self, num): + structs = [ + (17, 23, 6, numeric, num), + ] + self.content = Row.setStructs(structs=structs, content=self.content) + + def setSumOfValues(self, sum): + structs = [ + (23, 41, 18, numeric, sum), # Soma dos valores dos lotes + ] + self.content = Row.setStructs(structs=structs, content=self.content) + + def setSumOfValuesNonBarCodeTax(self, sum, otherSum, additionSum, totalSum): + structs = [ + (23, 37, 14, numeric, sum), # Soma dos valores principais dos lotes + (37, 51, 14, numeric, otherSum), # Soma dos valores outras entidades dos lotes + (51, 65, 14, numeric, additionSum), # Soma dos valores acréscimos dos lotes + (65, 79, 14, numeric, totalSum), # Soma dos valores totais dos lotes + ] + self.content = Row.setStructs(structs=structs, content=self.content) + + def setSenderBank(self, bank): + structs = [ + (0, 3, 3, numeric, bank.bankId), # Código do banco debitado + ] + self.content = Row.setStructs(structs=structs, content=self.content) + + def setPositionInLot(self, index): + structs = [ + (3, 7, 4, numeric, index) # Indica index do lote + ] + self.content = Row.setStructs(structs=structs, content=self.content) diff --git a/febraban/cnab240/itau/sisdeb/payment/__init__.py b/febraban/cnab240/itau/sisdeb/payment/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/febraban/cnab240/itau/sisdeb/payment/debit.py b/febraban/cnab240/itau/sisdeb/payment/debit.py new file mode 100644 index 0000000..3c46123 --- /dev/null +++ b/febraban/cnab240/itau/sisdeb/payment/debit.py @@ -0,0 +1,41 @@ +from .segmentA import SegmentA + + +class Debit: + + def __init__(self): + self.segmentA = SegmentA() + + def toString(self): + return self.segmentA.content + + def amountInCents(self): + return self.segmentA.amountInCents() + + def setSender(self, user): + self.segmentA.setSender(user) + + def setReceiver(self, user): + self.segmentA.setReceiver(user) + self.segmentA.setReceiverBank(user.bank) + + def setAmountInCents(self, value): + self.segmentA.setAmountInCents(value) + + def setPositionInLot(self, index): + self.segmentA.setPositionInLot(index) + + def setLot(self, lot): + self.segmentA.setLot(lot) + + def setScheduleDate(self, date): + self.segmentA.setScheduleDate(date) + + def setIdentifier(self, identifier): + self.segmentA.setIdentifier(identifier) + + def setFee(self, identifier): + self.segmentA.setFee(identifier) + + def setFeePrice(self, price): + self.segmentA.setFeePrice(price) diff --git a/febraban/cnab240/itau/sisdeb/payment/segmentA.py b/febraban/cnab240/itau/sisdeb/payment/segmentA.py new file mode 100644 index 0000000..85da67a --- /dev/null +++ b/febraban/cnab240/itau/sisdeb/payment/segmentA.py @@ -0,0 +1,89 @@ +# coding: utf-8 + +from ....characterType import numeric, alphaNumeric +from ....row import Row + + +class SegmentA: + + def __init__(self): + self.content = " " * 240 + self.defaultValues() + + def amountInCents(self): + return int(self.content[119:134]) + + def defaultValues(self): + structs = [ + ( 3, 7, 4, numeric, "1"), + ( 7, 8, 1, numeric, "3"), + ( 13, 14, 1, alphaNumeric, "A"), + ( 14, 20, 6, numeric, "0"), + (101, 104, 3, alphaNumeric, "REA"), + (104, 119, 15, numeric, "0"), + ] + self.content = Row.setStructs(structs=structs, content=self.content) + + def setSender(self, user): + structs = [ + (0, 3, 3, numeric, user.bank.bankId), # Código do # banco debitado + (216, 230, 14, numeric, user.identifier) + ] + self.content = Row.setStructs(structs=structs, content=self.content) + + def setReceiver(self, user): + structs = [ + ( 43, 73, 30, alphaNumeric, user.name), # Nome FAvorecido + ] + self.content = Row.setStructs(structs=structs, content=self.content) + + def setReceiverBank(self, bank): + structs = [ + (20, 23, 3, numeric, bank.bankId), # Código do banco do Favorecido + (23, 28, 5, numeric, bank.branchCode), # Agencia Favorecido + (29, 41, 12, numeric, bank.accountNumber), # Conta Favorecido + (42, 43, 1, numeric, bank.accountVerifier), # DAC FAvorecido + ] + self.content = Row.setStructs(structs=structs, content=self.content) + + def setAmountInCents(self, amount): + structs = [ + (119, 134, 15, numeric, amount) # Valor ao Favorecido + ] + self.content = Row.setStructs(structs=structs, content=self.content) + + def setPositionInLot(self, index): + structs = [ + (8, 13, 5, numeric, index), # Indica index do lote + ] + self.content = Row.setStructs(structs=structs, content=self.content) + + def setLot(self, lot): + structs = [ + (3, 7, 4, numeric, lot), + ] + self.content = Row.setStructs(structs=structs, content=self.content) + + def setScheduleDate(self, date): + structs = [ + (93, 101, 8, numeric, date) # Data para transferencia + ] + self.content = Row.setStructs(structs=structs, content=self.content) + + def setIdentifier(self, identifier): + structs = [ + (73, 93, 20, alphaNumeric, identifier) # Id da empresa na transação + ] + self.content = Row.setStructs(structs=structs, content=self.content) + + def setFee(self, identifier): + structs = [ + (177, 179, 2, numeric, identifier) # Mora 00 = isento + ] + self.content = Row.setStructs(structs=structs, content=self.content) + + def setFeePrice(self, price): + structs = [ + (179, 196, 17, numeric, price) # valor da mora + ] + self.content = Row.setStructs(structs=structs, content=self.content) diff --git a/febraban/cnab240/itau/sisdeb/result/__init__.py b/febraban/cnab240/itau/sisdeb/result/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/febraban/cnab240/itau/sisdeb/result/occurrences.py b/febraban/cnab240/itau/sisdeb/result/occurrences.py new file mode 100644 index 0000000..4016fd4 --- /dev/null +++ b/febraban/cnab240/itau/sisdeb/result/occurrences.py @@ -0,0 +1,117 @@ +# coding: utf-8 + +occurrences = { + "00": "PAGAMENTO EFETUADO", + "AE": "DATA DE PAGAMENTO ALTERADA", + "AG": "NÚMERO DO LOTE INVÁLIDO", + "AH": "NÚMERO SEQUENCIAL DO REGISTRO NO LOTE INVÁLIDO", + "AI": "PRODUTO DEMONSTRATIVO DE PAGAMENTO NÃO CONTRATADO", + "AJ": "TIPO DE MOVIMENTO INVÁLIDO", + "AL": "CÓDIGO DO BANCO FAVORECIDO INVÁLIDO", + "AM": "AGÊNCIA DO FAVORECIDO INVÁLIDA", + "AN": "CONTA CORRENTE DO FAVORECIDO INVÁLIDA / CONTA INVESTIMENTO EXTINTA EM 30/04/2011", + "AO": "NOME DO FAVORECIDO INVÁLIDO", + "AP": "DATA DE PAGAMENTO / DATA DE VALIDADE / HORA DE LANÇAMENTO / ARRECADAÇÃO / APURAÇÃO INVÁLIDA", + "AQ": "QUANTIDADE DE REGISTROS MAIOR QUE 999999", + "AR": "VALOR ARRECADADO / LANÇAMENTO INVÁLIDO", + "BC": "NOSSO NÚMERO INVÁLIDO", + "BD": "PAGAMENTO AGENDADO", + "BE": "PAGAMENTO AGENDADO COM FORMA ALTERADA PARA OP", + "BI": "CNPJ / CPF DO FAVORECIDO NO SEGMENTOJ-52 ou B INVÁLIDO", + "BL": "VALOR DA PARCELA INVÁLIDO", + "CD": "CNPJ / CPF INFORMADO DIVERGENTE DO CADASTRADO", + "CE": "PAGAMENTO CANCELADO", + "CF": "VALOR DO DOCUMENTO INVÁLIDO", + "CG": "VALOR DO ABATIMENTO INVÁLIDO", + "CH": "VALOR DO DESCONTO INVÁLIDO", + "CI": "CNPJ / CPF / IDENTIFICADOR / INSCRIÇÃO ESTADUAL / INSCRIÇÃO NO CAD / ICMS INVÁLIDO", + "CJ": "VALOR DA MULTA INVÁLIDO", + "CK": "TIPO DE INSCRIÇÃO INVÁLIDA", + "CL": "VALOR DO INSS INVÁLIDO", + "CM": "VALOR DO COFINS INVÁLIDO", + "CN": "CONTA NÃO CADASTRADA", + "CO": "VALOR DE OUTRAS ENTIDADES INVÁLIDO", + "CP": "CONFIRMAÇÃO DE OP CUMPRIDA", + "CQ": "SOMA DAS FATURAS DIFERE DO PAGAMENTO", + "CR": "VALOR DO CSLL INVÁLIDO", + "CS": "DATA DE VENCIMENTO DA FATURA INVÁLIDA", + "DA": "NÚMERO DE DEPEND. SALÁRIO FAMILIA INVALIDO", + "DB": "NÚMERO DE HORAS SEMANAIS INVÁLIDO", + "DC": "SALÁRIO DE CONTRIBUIÇÃO INSS INVÁLIDO", + "DD": "SALÁRIO DE CONTRIBUIÇÃO FGTS INVÁLIDO", + "DE": "VALOR TOTAL DOS PROVENTOS INVÁLIDO", + "DF": "VALOR TOTAL DOS DESCONTOS INVÁLIDO", + "DG": "VALOR LÍQUIDO NÃO NUMÉRICO", + "DH": "VALOR LIQ. INFORMADO DIFERE DO CALCULADO", + "DI": "VALOR DO SALÁRIO-BASE INVÁLIDO", + "DJ": "BASE DE CÁLCULO IRRF INVÁLIDA", + "DK": "BASE DE CÁLCULO FGTS INVÁLIDA", + "DL": "FORMA DE PAGAMENTO INCOMPATÍVEL COM HOLERITE", + "DM": "E-MAIL DO FAVORECIDO INVÁLIDO", + "DV": "DOC / TED DEVOLVIDO PELO BANCO FAVORECIDO", + "D0": "FINALIDADE DO HOLERITE INVÁLIDA", + "D1": "MÊS DE COMPETENCIA DO HOLERITE INVÁLIDA", + "D2": "DIA DA COMPETENCIA DO HOLETITE INVÁLIDA", + "D3": "CENTRO DE CUSTO INVÁLIDO", + "D4": "CAMPO NUMÉRICO DA FUNCIONAL INVÁLIDO", + "D5": "DATA INÍCIO DE FÉRIAS NÃO NUMÉRICA", + "D6": "DATA INÍCIO DE FÉRIAS INCONSISTENTE", + "D7": "DATA FIM DE FÉRIAS NÃO NUMÉRICO", + "D8": "DATA FIM DE FÉRIAS INCONSISTENTE", + "D9": "NÚMERO DE DEPENDENTES IR INVÁLIDO", + "EM": "CONFIRMAÇÃO DE OP EMITIDA", + "EX": "DEVOLUÇÃO DE OP NÃO SACADA PELO FAVORECIDO", + "E0": "TIPO DE MOVIMENTO HOLERITE INVÁLIDO", + "E1": "VALOR 01 DO HOLERITE / INFORME INVÁLIDO", + "E2": "VALOR 02 DO HOLERITE / INFORME INVÁLIDO", + "E3": "VALOR 03 DO HOLERITE / INFORME INVÁLIDO", + "E4": "VALOR 04 DO HOLERITE / INFORME INVÁLIDO", + "FC": "PAGAMENTO EFETUADO ATRAVÉS DE FINANCIAMENTO COMPROR", + "FD": "PAGAMENTO EFETUADO ATRAVÉS DE FINANCIAMENTO DESCOMPROR", + "HA": "ERRO NO HEADER DE ARQUIVO", + "HM": "ERRO NO HEADER DE LOTE", + "IB": "VALOR E/OU DATA DO DOCUMENTO INVÁLIDO", + "IC": "VALOR DO ABATIMENTO INVÁLIDO", + "ID": "VALOR DO DESCONTO INVÁLIDO", + "IE": "VALOR DA MORA INVÁLIDO", + "IF": "VALOR DA MULTA INVÁLIDO", + "IG": "VALOR DA DEDUÇÃO INVÁLIDO", + "IH": "VALOR DO ACRÉSCIMO INVÁLIDO", + "II": "DATA DE VENCIMENTO INVÁLIDA", + "IJ": "COMPETÊNCIA / PERÍODO REFERÊNCIA / PARCELA INVÁLIDA", + "IK": "TRIBUTO NÃO LIQUIDÁVEL VIA SISPAG OU NÃO CONVENIADO COM ITAÚ", + "IL": "CÓDIGO DE PAGAMENTO / EMPRESA /RECEITA INVÁLIDO", + "IM": "TIPO X FORMA NÃO COMPATÍVEL", + "IN": "BANCO/AGENCIA NÃO CADASTRADOS", + "IO": "DAC / VALOR / COMPETÊNCIA / IDENTIFICADOR DO LACRE INVÁLIDO", + "IP": "DAC DO CÓDIGO DE BARRAS INVÁLIDO", + "IQ": "DÍVIDA ATIVA OU NÚMERO DE ETIQUETA INVÁLIDO", + "IR": "PAGAMENTO ALTERADO", + "IS": "CONCESSIONÁRIA NÃO CONVENIADA COM ITAÚ", + "IT": "VALOR DO TRIBUTO INVÁLIDO", + "IU": "VALOR DA RECEITA BRUTA ACUMULADA INVÁLIDO", + "IV": "NÚMERO DO DOCUMENTO ORIGEM / REFERÊNCIA INVÁLIDO", + "IX": "CÓDIGO DO PRODUTO INVÁLIDO", + "LA": "DATA DE PAGAMENTO DE UM LOTE ALTERADA", + "LC": "LOTE DE PAGAMENTOS CANCELADO", + "NA": "PAGAMENTO CANCELADO POR FALTA DE AUTORIZAÇÃO", + "NB": "IDENTIFICAÇÃO DO TRIBUTO INVÁLIDA", + "NC": "EXERCÍCIO (ANO BASE) INVÁLIDO", + "ND": "CÓDIGO RENAVAM NÃO ENCONTRADO/INVÁLIDO", + "NE": "UF INVÁLIDA", + "NF": "CÓDIGO DO MUNICÍPIO INVÁLIDO", + "NG": "PLACA INVÁLIDA", + "NH": "OPÇÃO/PARCELA DE PAGAMENTO INVÁLIDA", + "NI": "TRIBUTO JÁ FOI PAGO OU ESTÁ VENCIDO", + "NR": "OPERAÇÃO NÃO REALIZADA RJ REGISTRO REJEITADO", + "PD": "AQUISIÇÃO CONFIRMADA (EQUIVALE A OCORRÊNCIA 02 NO LAYOUT DE DISCO SACADO)", + "RJ": "REGISTRO REJEITADO", + "RS": "PAGAMENTO DISPONÍVEL PARA ANTECIPAÇÃO NO RISCO SACADO – MODALIDADE RISCO SACADO PÓS AUTORIZADO", + "SS": "PAGAMENTO CANCELADO POR INSUFICIÊNCIA DE SALDO/LIMITE DIÁRIO DE PAGTO", + "TA": "LOTE NÃO ACEITO - TOTAIS DO LOTE COM DIFERENÇA", + "TI": "TITULARIDADE INVÁLIDA", + "X1": "FORMA INCOMPATÍVEL COM LAYOUT 010", + "X2": "NÚMERO DA NOTA FISCAL INVÁLIDO", + "X3": "IDENTIFICADOR DE NF/CNPJ INVÁLIDO", + "X4": "FORMA 32 INVÁLIDA", +} \ No newline at end of file diff --git a/febraban/cnab240/itau/sisdeb/result/parser.py b/febraban/cnab240/itau/sisdeb/result/parser.py new file mode 100644 index 0000000..769e816 --- /dev/null +++ b/febraban/cnab240/itau/sisdeb/result/parser.py @@ -0,0 +1,165 @@ +from febraban.cnab240.libs.paymentType import NonBarcodeTaxPayment +from .occurrences import occurrences + + +class PaymentResponseStatus: + + success = "success" + failed = "failed" + scheduled = "scheduled" + unknown = "unknown" + + +class PaymentType: + + transfer = "transfer" + chargePayment = "charge-payment" + nonBarcodeTaxPayment = "tax-payment" + barcodePayment = "barcode-payment" + debit = "debit" + + +class PaymentResponse: + + def __init__(self, identifier=None, occurrences=None, content=None, authentication=None, amountInCents=None, paymentType=None, nonBarcodeTax=None): + self.identifier = identifier + self.occurrences = occurrences + self.content = content or [] + self.authentication = authentication + self.amountInCents = amountInCents + self.type = paymentType + self.nonBarcodeTax = nonBarcodeTax + + def occurrencesText(self): + return [occurrences[occurrenceId] for occurrenceId in self.occurrences] + + def occurrencesTextAtIndex(self, index): + occurrenceId = self.occurrences[index] + return occurrences[occurrenceId] + + def status(self): + if "00" in self.occurrences: + return PaymentResponseStatus.success + if "BD" in self.occurrences: + return PaymentResponseStatus.scheduled + if [code in self.occurrences for code in ["RJ", "DV"]].count(True) > 0: + return PaymentResponseStatus.failed + return PaymentResponseStatus.unknown + + def contentText(self, breakLine="\n"): + return breakLine.join(self.content) + + +class PaymentParser: + + @classmethod + def parseFile(cls, file): + lines = file.readlines() + return cls.parseLines(lines) + + @classmethod + def parseText(cls, text): + lines = text.splitlines()[:-1] + return cls.parseLines(lines) + + @classmethod + def parseLines(cls, lines): + result = [] + currentResponse = None + for line in lines: + # headerlot + if line[7] == "1": + if line[8] != "D": + # if not D -> current lot doesn't represent a debit order + break + if line[7] in ["0", "1", "9"]: + continue + + if line[7] == "3" and line[13] in ["A", "J", "O", "N"]: + if currentResponse is not None: + result.append(currentResponse) + currentResponse = PaymentResponse() + + if line[7] == "3" and line[13] == "A": + currentResponse.content.append(line) + currentResponse.identifier = cls._getIdentifierSegmentA(line) + currentResponse.occurrences = cls._getOccurrences(line) + currentResponse.amountInCents = cls._getAmountSegmentA(line) + currentResponse.type = PaymentType.debit + elif line[7] == "3" and line[13] == "J": + currentResponse.content.append(line) + currentResponse.identifier = cls._getIdentifierSegmentJ(line) + currentResponse.occurrences = cls._getOccurrences(line) + currentResponse.amountInCents = cls._getAmountSegmentJ(line) + currentResponse.type = PaymentType.chargePayment + elif line[7] == "3" and line[13] == "O": + currentResponse.content.append(line) + currentResponse.identifier = cls._getIdentifierSegmentO(line) + currentResponse.occurrences = cls._getOccurrences(line) + currentResponse.amountInCents = cls._getAmountSegmentO(line) + currentResponse.type = PaymentType.barcodePayment + elif line[7] == "3" and line[13] == "N": + currentResponse.content.append(line) + currentResponse.identifier = cls._getIdentifierSegmentN(line) + currentResponse.occurrences = cls._getOccurrences(line) + currentResponse.nonBarcodeTax = cls._getNonBarcodeTaxSegmentN(line) + currentResponse.type = PaymentType.nonBarcodeTaxPayment + elif line[7] == "3" and line[13] == "Z": + currentResponse.content.append(line) + currentResponse.authentication = cls._getAuthentication(line) + + if line[7] == "5" and currentResponse is not None: + result.append(currentResponse) + currentResponse = None + + return result + + @classmethod + def _getOccurrences(cls, line): + occurrencesString = line[230:240].strip() + return cls._splitString(occurrencesString) + + @classmethod + def _splitString(cls, string): + return [string[i:i+2] for i in range(0, len(string), 2)] + + @classmethod + def _getAmountSegmentA(cls, line): + return int(line[119:134].strip()) + + @classmethod + def _getAmountSegmentJ(cls, line): + return int(line[152:167].strip()) + + @classmethod + def _getAmountSegmentO(cls, line): + return int(line[144:159].strip()) + + @classmethod + def _getIdentifierSegmentA(self, line): + return line[73:93].strip() + + @classmethod + def _getIdentifierSegmentJ(self, line): + return line[182:202].strip() + + @classmethod + def _getIdentifierSegmentO(self, line): + return line[174:194].strip() + + @classmethod + def _getIdentifierSegmentN(self, line): + return line[195:215].strip() + + @classmethod + def _getAuthentication(cls, line): + return line[14:78].strip() + + @classmethod + def _getNonBarcodeTaxSegmentN(self, line): + taxTypeId = line[17:19].strip() + return { + "01": NonBarcodeTaxPayment.gps, + "02": NonBarcodeTaxPayment.darf, + "11": NonBarcodeTaxPayment.fgts, + }[taxTypeId] diff --git a/febraban/cnab240/itau/sisdeb/sisdeb_cnab_240.pdf b/febraban/cnab240/itau/sisdeb/sisdeb_cnab_240.pdf new file mode 100644 index 0000000..a79e9da Binary files /dev/null and b/febraban/cnab240/itau/sisdeb/sisdeb_cnab_240.pdf differ diff --git a/sample-deb-parser.py b/sample-deb-parser.py new file mode 100644 index 0000000..abf5b42 --- /dev/null +++ b/sample-deb-parser.py @@ -0,0 +1,15 @@ +from febraban.cnab240.itau.sisdeb import PaymentParser + + +file = open("output-debit.RET", "r") + +responses = PaymentParser.parseFile(file) + +for response in responses: + print "-------------------------------------------------------------------" + print response.identifier + print response.authentication + print response.status() + print response.amountInCents + print response.type + print response.contentText() diff --git a/sample-debit.py b/sample-debit.py new file mode 100644 index 0000000..a8c0222 --- /dev/null +++ b/sample-debit.py @@ -0,0 +1,76 @@ + +# coding: utf-8 + +from febraban.cnab240.itau.sisdeb import Debit, File +from febraban.cnab240.itau.sisdeb.file.lot import Lot +from febraban.cnab240.user import User, UserAddress, UserBank + + +sender = User( + name="YOUR COMPANY NAME HERE", + identifier="12345678901234", + bank=UserBank( + bankId="341", + branchCode="4321", + accountNumber="12345678", + accountVerifier="9" + ), + address=UserAddress( + streetLine1="AV PAULISTA 1000", + city="SAO PAULO", + stateCode="SP", + zipCode="01310000" + ) +) + +receiver1 = User( + name="RECEIVER NAME HERE", + identifier="01234567890", + bank=UserBank( + bankId="341", + branchCode="1234", + accountNumber="12345", + accountVerifier="9" + ) +) + +receiver2 = User( + name="RECEIVER NAME HERE", + identifier="01234567890", + bank=UserBank( + bankId="341", + branchCode="1234", + accountNumber="12345", + accountVerifier="9" + ) +) + +receivers = [receiver1, receiver2] + +file = File() +file.setSender(sender) +file.setBankAgreementCode('CONV56789ENIO') + +lot = Lot() + +lot.setSender(sender) +lot.setBankAgreementCode('CONV56789ENIO') +lot.setHeaderLotType( + kind="05", #Tipo de serviço + method="50" #FORMALANCTO +) + +for receiver in receivers: + payment = Debit() + payment.setSender(sender) + payment.setReceiver(receiver) + payment.setAmountInCents("10000") + payment.setFee('00') + payment.setFeePrice("00000000000000000") + payment.setScheduleDate("06052020") + payment.setIdentifier("ID1234567890345") + lot.add(register=payment) + +file.addLot(lot) +file.output(fileName="output-debit.REM", path="/../../") +