style(__init__): Formateado PEP8

This commit is contained in:
sinergia 2024-08-06 13:28:59 -05:00
parent a0321020c7
commit f08954ee43

View File

@ -1,12 +1,14 @@
# This file is part of facho. The COPYRIGHT file at the top level of # This file is part of facho. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms. # this repository contains the full copyright notices and license terms.
import hashlib
from functools import reduce # import hashlib
import copy # from functools import reduce
# import copy
import dataclasses import dataclasses
from dataclasses import dataclass, field from dataclasses import dataclass, field
from datetime import datetime, date from datetime import datetime, date
from collections import defaultdict # from collections import defaultdict
import decimal import decimal
from decimal import Decimal from decimal import Decimal
import typing import typing
@ -14,9 +16,11 @@ from ..data.dian import codelist
DECIMAL_PRECISION = 6 DECIMAL_PRECISION = 6
class AmountCurrencyError(TypeError): class AmountCurrencyError(TypeError):
pass pass
@dataclass @dataclass
class Currency: class Currency:
code: str code: str
@ -27,6 +31,7 @@ class Currency:
def __str__(self): def __str__(self):
return self.code return self.code
class Collection: class Collection:
def __init__(self, array): def __init__(self, array):
@ -43,6 +48,7 @@ class Collection:
def sum(self): def sum(self):
return sum(self.array) return sum(self.array)
class AmountCollection(Collection): class AmountCollection(Collection):
def sum(self): def sum(self):
@ -51,10 +57,13 @@ class AmountCollection(Collection):
total += v total += v
return total return total
class Amount:
def __init__(self, amount: int or float or str or Amount, currency: Currency = Currency('COP')):
#DIAN 1.7.-2020: 1.2.3.1 class Amount:
def __init__(
self, amount: typing.Union[int, float, str, "Amount"],
currency: Currency = Currency('COP')):
# DIAN 1.7.-2020: 1.2.3.1
if isinstance(amount, Amount): if isinstance(amount, Amount):
if amount < Amount(0.0): if amount < Amount(0.0):
raise ValueError('amount must be positive >= 0') raise ValueError('amount must be positive >= 0')
@ -65,14 +74,16 @@ class Amount:
if float(amount) < 0: if float(amount) < 0:
raise ValueError('amount must be positive >= 0') raise ValueError('amount must be positive >= 0')
self.amount = Decimal(amount, decimal.Context(prec=DECIMAL_PRECISION, self.amount = Decimal(
#DIAN 1.7.-2020: 1.2.1.1 amount, decimal.Context(
rounding=decimal.ROUND_HALF_EVEN )) prec=DECIMAL_PRECISION,
# DIAN 1.7.-2020: 1.2.1.1
rounding=decimal.ROUND_HALF_EVEN))
self.currency = currency self.currency = currency
def fromNumber(self, val): def fromNumber(self, val):
return Amount(val, currency=self.currency) return Amount(val, currency=self.currency)
def round(self, prec): def round(self, prec):
return Amount(round(self.amount, prec), currency=self.currency) return Amount(round(self.amount, prec), currency=self.currency)
@ -90,7 +101,8 @@ class Amount:
def __eq__(self, other): def __eq__(self, other):
if not self.is_same_currency(other): if not self.is_same_currency(other):
raise AmountCurrencyError() raise AmountCurrencyError()
return round(self.amount, DECIMAL_PRECISION) == round(other.amount, DECIMAL_PRECISION) return round(self.amount, DECIMAL_PRECISION) == round(
other.amount, DECIMAL_PRECISION)
def _cast(self, val): def _cast(self, val):
if type(val) in [int, float]: if type(val) in [int, float]:
@ -98,7 +110,7 @@ class Amount:
if isinstance(val, Amount): if isinstance(val, Amount):
return val return val
raise TypeError("cant cast to amount") raise TypeError("cant cast to amount")
def __add__(self, rother): def __add__(self, rother):
other = self._cast(rother) other = self._cast(rother)
if not self.is_same_currency(other): if not self.is_same_currency(other):
@ -122,14 +134,14 @@ class Amount:
def truncate_as_string(self, prec): def truncate_as_string(self, prec):
parts = str(self.float()).split('.', 1) parts = str(self.float()).split('.', 1)
return '%s.%s' % (parts[0], parts[1][0:prec].ljust(prec,'0')) return '%s.%s' % (parts[0], parts[1][0:prec].ljust(prec, '0'))
def float(self): def float(self):
return float(round(self.amount, DECIMAL_PRECISION)) return float(round(self.amount, DECIMAL_PRECISION))
class Quantity: class Quantity:
def __init__(self, val, code): def __init__(self, val, code):
if type(val) not in [float, int]: if type(val) not in [float, int]:
raise ValueError('val expected int or float') raise ValueError('val expected int or float')
@ -151,6 +163,7 @@ class Quantity:
def __repr__(self): def __repr__(self):
return str(self) return str(self)
@dataclass @dataclass
class Item: class Item:
scheme_name: str scheme_name: str
@ -175,9 +188,9 @@ class UNSPSCItem(Item):
description=description, description=description,
scheme_name='UNSPSC', scheme_name='UNSPSC',
scheme_id='001', scheme_id='001',
scheme_agency_id='10') scheme_agency_id='10')
@dataclass @dataclass
class Country: class Country:
code: str code: str
@ -188,6 +201,7 @@ class Country:
raise ValueError("code [%s] not found" % (self.code)) raise ValueError("code [%s] not found" % (self.code))
self.name = codelist.Paises[self.code]['name'] self.name = codelist.Paises[self.code]['name']
@dataclass @dataclass
class CountrySubentity: class CountrySubentity:
code: str code: str
@ -198,6 +212,7 @@ class CountrySubentity:
raise ValueError("code [%s] not found" % (self.code)) raise ValueError("code [%s] not found" % (self.code))
self.name = codelist.Departamento[self.code]['name'] self.name = codelist.Departamento[self.code]['name']
@dataclass @dataclass
class City: class City:
code: str code: str
@ -208,19 +223,23 @@ class City:
raise ValueError("code [%s] not found" % (self.code)) raise ValueError("code [%s] not found" % (self.code))
self.name = codelist.Municipio[self.code]['name'] self.name = codelist.Municipio[self.code]['name']
@dataclass @dataclass
class PostalZone: class PostalZone:
code: str = '' code: str = ''
@dataclass @dataclass
class Address: class Address:
name: str name: str
street: str = '' street: str = ''
city: City = field(default_factory=lambda: City('05001')) city: City = field(default_factory=lambda: City('05001'))
country: Country = field(default_factory=lambda: Country('CO')) country: Country = field(default_factory=lambda: Country('CO'))
countrysubentity: CountrySubentity = field(default_factory=lambda: CountrySubentity('05')) countrysubentity: CountrySubentity = field(
default_factory=lambda: CountrySubentity('05'))
postalzone: PostalZone = field(default_factory=lambda: PostalZone('')) postalzone: PostalZone = field(default_factory=lambda: PostalZone(''))
@dataclass @dataclass
class PartyIdentification: class PartyIdentification:
number: str number: str
@ -240,6 +259,7 @@ class PartyIdentification:
if self.type_fiscal not in codelist.TipoIdFiscal: if self.type_fiscal not in codelist.TipoIdFiscal:
raise ValueError("type_fiscal [%s] not found" % (self.type_fiscal)) raise ValueError("type_fiscal [%s] not found" % (self.type_fiscal))
@dataclass @dataclass
class Responsability: class Responsability:
codes: list codes: list
@ -269,6 +289,7 @@ class TaxScheme:
raise ValueError("code not found") raise ValueError("code not found")
self.name = codelist.TipoImpuesto[self.code]['name'] self.name = codelist.TipoImpuesto[self.code]['name']
@dataclass @dataclass
class Party: class Party:
name: str name: str
@ -334,6 +355,7 @@ class TaxTotalOmit(TaxTotal):
def calculate(self, invline): def calculate(self, invline):
pass pass
@dataclass @dataclass
class WithholdingTaxSubTotal: class WithholdingTaxSubTotal:
percent: float percent: float
@ -344,6 +366,7 @@ class WithholdingTaxSubTotal:
if self.percent is not None: if self.percent is not None:
self.tax_amount = invline.total_amount * Amount(self.percent / 100) self.tax_amount = invline.total_amount * Amount(self.percent / 100)
@dataclass @dataclass
class WithholdingTaxTotal: class WithholdingTaxTotal:
subtotals: list subtotals: list
@ -356,7 +379,8 @@ class WithholdingTaxTotal:
for subtax in self.subtotals: for subtax in self.subtotals:
subtax.calculate(invline) subtax.calculate(invline)
self.tax_amount += subtax.tax_amount self.tax_amount += subtax.tax_amount
class WithholdingTaxTotalOmit(WithholdingTaxTotal): class WithholdingTaxTotalOmit(WithholdingTaxTotal):
def __init__(self): def __init__(self):
super().__init__([]) super().__init__([])
@ -364,6 +388,7 @@ class WithholdingTaxTotalOmit(WithholdingTaxTotal):
def calculate(self, invline): def calculate(self, invline):
pass pass
@dataclass @dataclass
class Price: class Price:
amount: Amount amount: Amount
@ -379,6 +404,7 @@ class Price:
self.amount *= self.quantity self.amount *= self.quantity
@dataclass @dataclass
class PaymentMean: class PaymentMean:
DEBIT = '01' DEBIT = '01'
@ -396,15 +422,17 @@ class PaymentMean:
@dataclass @dataclass
class PrePaidPayment: class PrePaidPayment:
#DIAN 1.7.-2020: FBD03 # DIAN 1.7.-2020: FBD03
paid_amount: Amount = field(default_factory=lambda: Amount(0.0)) paid_amount: Amount = field(default_factory=lambda: Amount(0.0))
@dataclass @dataclass
class BillingResponse: class BillingResponse:
id: str id: str
code: str code: str
description: str description: str
class SupportDocumentCreditNoteResponse(BillingResponse): class SupportDocumentCreditNoteResponse(BillingResponse):
""" """
ReferenceID: Identifica la sección del Documento ReferenceID: Identifica la sección del Documento
@ -414,13 +442,13 @@ class SupportDocumentCreditNoteResponse(BillingResponse):
""" """
@dataclass @dataclass
class BillingReference: class BillingReference:
ident: str ident: str
uuid: str uuid: str
date: date date: date
class CreditNoteDocumentReference(BillingReference): class CreditNoteDocumentReference(BillingReference):
""" """
ident: Prefijo + Numero de la factura relacionada ident: Prefijo + Numero de la factura relacionada
@ -428,6 +456,7 @@ class CreditNoteDocumentReference(BillingReference):
date: fecha de emision de la factura relacionada date: fecha de emision de la factura relacionada
""" """
class DebitNoteDocumentReference(BillingReference): class DebitNoteDocumentReference(BillingReference):
""" """
ident: Prefijo + Numero de la factura relacionada ident: Prefijo + Numero de la factura relacionada
@ -435,6 +464,7 @@ class DebitNoteDocumentReference(BillingReference):
date: fecha de emision de la factura relacionada date: fecha de emision de la factura relacionada
""" """
class InvoiceDocumentReference(BillingReference): class InvoiceDocumentReference(BillingReference):
""" """
ident: Prefijo + Numero de la nota credito relacionada ident: Prefijo + Numero de la nota credito relacionada
@ -442,6 +472,7 @@ class InvoiceDocumentReference(BillingReference):
date: fecha de emision de la nota credito relacionada date: fecha de emision de la nota credito relacionada
""" """
@dataclass @dataclass
class AllowanceChargeReason: class AllowanceChargeReason:
code: str code: str
@ -468,10 +499,12 @@ class AllowanceCharge:
default_factory=lambda: Amount(1.0)) default_factory=lambda: Amount(1.0))
def isCharge(self): def isCharge(self):
return self.charge_indicator == True charge_indicator = self.charge_indicator is True
return charge_indicator
def isDiscount(self): def isDiscount(self):
return self.charge_indicator == False charge_indicator = self.charge_indicator is False
return charge_indicator
def asCharge(self): def asCharge(self):
self.charge_indicator = True self.charge_indicator = True
@ -485,11 +518,13 @@ class AllowanceCharge:
def set_base_amount(self, amount): def set_base_amount(self, amount):
self.base_amount = amount self.base_amount = amount
class AllowanceChargeAsDiscount(AllowanceCharge): class AllowanceChargeAsDiscount(AllowanceCharge):
def __init__(self, amount: Amount = Amount(0.0)): def __init__(self, amount: Amount = Amount(0.0)):
self.charge_indicator = False self.charge_indicator = False
self.amount = amount self.amount = amount
@dataclass @dataclass
class InvoiceLine: class InvoiceLine:
# RESOLUCION 0004: pagina 155 # RESOLUCION 0004: pagina 155
@ -503,7 +538,8 @@ class InvoiceLine:
# de subtotal # de subtotal
tax: typing.Optional[TaxTotal] tax: typing.Optional[TaxTotal]
withholding: typing.Optional[WithholdingTaxTotal] withholding: typing.Optional[WithholdingTaxTotal]
allowance_charge: typing.List[AllowanceCharge] = dataclasses.field(default_factory=list) allowance_charge: typing.List[AllowanceCharge] = dataclasses.field(
default_factory=list)
def add_allowance_charge(self, charge): def add_allowance_charge(self, charge):
if not isinstance(charge, AllowanceCharge): if not isinstance(charge, AllowanceCharge):
@ -514,7 +550,7 @@ class InvoiceLine:
@property @property
def total_amount_without_charge(self): def total_amount_without_charge(self):
return (self.quantity * self.price.amount) return (self.quantity * self.price.amount)
@property @property
def total_amount(self): def total_amount(self):
charge = AmountCollection(self.allowance_charge)\ charge = AmountCollection(self.allowance_charge)\
@ -568,6 +604,7 @@ class InvoiceLine:
if self.withholding is None: if self.withholding is None:
self.withholding = WithholdingTaxTotalOmit() self.withholding = WithholdingTaxTotalOmit()
@dataclass @dataclass
class LegalMonetaryTotal: class LegalMonetaryTotal:
line_extension_amount: Amount = field(default_factory=lambda: Amount(0.0)) line_extension_amount: Amount = field(default_factory=lambda: Amount(0.0))
@ -579,7 +616,7 @@ class LegalMonetaryTotal:
prepaid_amount: Amount = field(default_factory=lambda: Amount(0.0)) prepaid_amount: Amount = field(default_factory=lambda: Amount(0.0))
def calculate(self): def calculate(self):
#DIAN 1.7.-2020: FAU14 # DIAN 1.7.-2020: FAU14
self.payable_amount = \ self.payable_amount = \
self.tax_inclusive_amount \ self.tax_inclusive_amount \
+ self.allowance_total_amount \ + self.allowance_total_amount \
@ -587,26 +624,29 @@ class LegalMonetaryTotal:
- self.prepaid_amount - self.prepaid_amount
class NationalSalesInvoiceDocumentType(str): class NationalSalesInvoiceDocumentType(str):
def __str__(self): def __str__(self):
# 6.1.3 # 6.1.3
return '01' return '01'
class CreditNoteDocumentType(str): class CreditNoteDocumentType(str):
def __str__(self): def __str__(self):
# 6.1.3 # 6.1.3
return '91' return '91'
class DebitNoteDocumentType(str): class DebitNoteDocumentType(str):
def __str__(self): def __str__(self):
# 6.1.3 # 6.1.3
return '92' return '92'
class CreditNoteSupportDocumentType(str): class CreditNoteSupportDocumentType(str):
def __str__(self): def __str__(self):
return '95' return '95'
class Invoice: class Invoice:
def __init__(self, type_code: str): def __init__(self, type_code: str):
if str(type_code) not in codelist.TipoDocumento: if str(type_code) not in codelist.TipoDocumento:
@ -652,7 +692,8 @@ class Invoice:
if len(prefix) <= 4: if len(prefix) <= 4:
self.invoice_ident_prefix = prefix self.invoice_ident_prefix = prefix
else: else:
raise ValueError('ident prefix failed to get, expected 0 to 4 chars') raise ValueError(
'ident prefix failed to get, expected 0 to 4 chars')
def set_ident(self, ident: str): def set_ident(self, ident: str):
""" """
@ -683,7 +724,7 @@ class Invoice:
def _get_codelist_tipo_operacion(self): def _get_codelist_tipo_operacion(self):
return codelist.TipoOperacionF return codelist.TipoOperacionF
def set_operation_type(self, operation): def set_operation_type(self, operation):
if operation not in self._get_codelist_tipo_operacion(): if operation not in self._get_codelist_tipo_operacion():
raise ValueError("operation not found") raise ValueError("operation not found")
@ -705,7 +746,6 @@ class Invoice:
def set_discrepancy_response(self, billing_response: BillingResponse): def set_discrepancy_response(self, billing_response: BillingResponse):
self.invoice_discrepancy_response = billing_response self.invoice_discrepancy_response = billing_response
def accept(self, visitor): def accept(self, visitor):
visitor.visit_payment_mean(self.invoice_payment_mean) visitor.visit_payment_mean(self.invoice_payment_mean)
visitor.visit_customer(self.invoice_customer) visitor.visit_customer(self.invoice_customer)
@ -717,29 +757,34 @@ class Invoice:
def _calculate_legal_monetary_total(self): def _calculate_legal_monetary_total(self):
for invline in self.invoice_lines: for invline in self.invoice_lines:
self.invoice_legal_monetary_total.line_extension_amount += invline.total_amount self.invoice_legal_monetary_total.line_extension_amount +=\
self.invoice_legal_monetary_total.tax_exclusive_amount += invline.total_tax_exclusive_amount invline.total_amount
#DIAN 1.7.-2020: FAU6 self.invoice_legal_monetary_total.tax_exclusive_amount +=\
self.invoice_legal_monetary_total.tax_inclusive_amount += invline.total_tax_inclusive_amount invline.total_tax_exclusive_amount
# DIAN 1.7.-2020: FAU6
self.invoice_legal_monetary_total.tax_inclusive_amount +=\
invline.total_tax_inclusive_amount
#DIAN 1.7.-2020: FAU08 # DIAN 1.7.-2020: FAU08
self.invoice_legal_monetary_total.allowance_total_amount = AmountCollection(self.invoice_allowance_charge)\ self.invoice_legal_monetary_total.allowance_total_amount =\
AmountCollection(self.invoice_allowance_charge)\
.filter(lambda charge: charge.isDiscount())\ .filter(lambda charge: charge.isDiscount())\
.map(lambda charge: charge.amount)\ .map(lambda charge: charge.amount)\
.sum() .sum()
#DIAN 1.7.-2020: FAU10 # DIAN 1.7.-2020: FAU10
self.invoice_legal_monetary_total.charge_total_amount = AmountCollection(self.invoice_allowance_charge)\ self.invoice_legal_monetary_total.charge_total_amount =\
AmountCollection(self.invoice_allowance_charge)\
.filter(lambda charge: charge.isCharge())\ .filter(lambda charge: charge.isCharge())\
.map(lambda charge: charge.amount)\ .map(lambda charge: charge.amount)\
.sum() .sum()
#DIAN 1.7.-2020: FAU12 # DIAN 1.7.-2020: FAU12
self.invoice_legal_monetary_total.prepaid_amount = AmountCollection(self.invoice_prepaid_payment)\ self.invoice_legal_monetary_total.prepaid_amount = AmountCollection(
.map(lambda paid: paid.paid_amount)\ self.invoice_prepaid_payment).map(
.sum() lambda paid: paid.paid_amount).sum()
#DIAN 1.7.-2020: FAU14 # DIAN 1.7.-2020: FAU14
self.invoice_legal_monetary_total.calculate() self.invoice_legal_monetary_total.calculate()
def _refresh_charges_base_amount(self): def _refresh_charges_base_amount(self):
@ -747,18 +792,21 @@ class Invoice:
for invline in self.invoice_lines: for invline in self.invoice_lines:
if invline.allowance_charge: if invline.allowance_charge:
# TODO actualmente solo uno de los cargos es permitido # TODO actualmente solo uno de los cargos es permitido
raise ValueError('allowance charge in invoice exclude invoice line') raise ValueError(
'allowance charge in invoice exclude invoice line')
# cargos a nivel de factura # cargos a nivel de factura
for charge in self.invoice_allowance_charge: for charge in self.invoice_allowance_charge:
charge.set_base_amount(self.invoice_legal_monetary_total.line_extension_amount) charge.set_base_amount(
self.invoice_legal_monetary_total.line_extension_amount)
def calculate(self): def calculate(self):
for invline in self.invoice_lines: for invline in self.invoice_lines:
invline.calculate() invline.calculate()
self._calculate_legal_monetary_total() self._calculate_legal_monetary_total()
self._refresh_charges_base_amount() self._refresh_charges_base_amount()
class NationalSalesInvoice(Invoice): class NationalSalesInvoice(Invoice):
def __init__(self): def __init__(self):
super().__init__(NationalSalesInvoiceDocumentType()) super().__init__(NationalSalesInvoiceDocumentType())
@ -774,7 +822,7 @@ class CreditNote(Invoice):
def _get_codelist_tipo_operacion(self): def _get_codelist_tipo_operacion(self):
return codelist.TipoOperacionNC return codelist.TipoOperacionNC
def _check_ident_prefix(self, prefix): def _check_ident_prefix(self, prefix):
if len(prefix) != 6: if len(prefix) != 6:
raise ValueError('prefix must be 6 length') raise ValueError('prefix must be 6 length')
@ -803,12 +851,15 @@ class DebitNote(Invoice):
if not self.invoice_ident_prefix: if not self.invoice_ident_prefix:
self.invoice_ident_prefix = self.invoice_ident[0:6] self.invoice_ident_prefix = self.invoice_ident[0:6]
class SupportDocument(Invoice): class SupportDocument(Invoice):
pass pass
class SupportDocumentCreditNote(SupportDocument): class SupportDocumentCreditNote(SupportDocument):
def __init__(self, invoice_document_reference: BillingReference, def __init__(
invoice_discrepancy_response: BillingResponse): self, invoice_document_reference: BillingReference,
invoice_discrepancy_response: BillingResponse):
super().__init__(CreditNoteSupportDocumentType()) super().__init__(CreditNoteSupportDocumentType())
if not isinstance(invoice_document_reference, BillingReference): if not isinstance(invoice_document_reference, BillingReference):