Se permite informar distintos impuestos en linea de factura

This commit is contained in:
sinergia 2023-10-24 16:27:31 -05:00
parent 71197dd031
commit 9eb010092c
1 changed files with 174 additions and 177 deletions

View File

@ -28,6 +28,7 @@ from dateutil import tz
Bogota = tz.gettz("America/Bogota")
class Cron(metaclass=PoolMeta):
__name__ = 'ir.cron'
@ -37,26 +38,26 @@ class Cron(metaclass=PoolMeta):
cls.method.selection.extend([
('account.invoice|fe_delivery', 'FE Delivery')
])
class Invoice(metaclass=PoolMeta):
__name__ = 'account.invoice'
_states_readonly = {'readonly': Eval('state') != 'draft'}
# TODO adicionar atributo fe_identifier y permitir seleccion desde form
fe_delivery_state = fields.Selection([
('draft', 'Draft'),
('queued', 'Queued'), # local encola
('delivered', 'Delivered'), # remoto encola
('exception', 'Exception'), # local exception
('failure', 'Failure'), # remoto fallo
('done', 'Done') # remoto ok
('queued', 'Queued'), # local encola
('delivered', 'Delivered'), # remoto encola
('exception', 'Exception'), # local exception
('failure', 'Failure'), # remoto fallo
('done', 'Done') # remoto ok
], 'Delivery State', states=_states_readonly)
fe_delivery_trackid = fields.Char('Delivery TrackID',
states=_states_readonly)
fe_delivery_status_description = fields.Char('Status Description',
states=_states_readonly)
states=_states_readonly)
fe_delivery_error_message = fields.Text('Error Message',
states=_states_readonly)
fe_delivery_checked_at = fields.DateTime('Delivery Checked At',
@ -78,10 +79,10 @@ class Invoice(metaclass=PoolMeta):
('10', 'Estándar'),
('20', 'Nota Crédito que referencia una factura electrónica.'),
('30', 'Nota Débito que referencia una factura electrónica.'),
],'Operation Type',
], 'Operation Type',
states={
'readonly': (Eval('state') != 'draft')
},
},
depends=['state'])
fe_document_reference = fields.Many2One('account.invoice',
@ -103,49 +104,48 @@ class Invoice(metaclass=PoolMeta):
states={
'required': True,
'readonly': (Eval('state') != 'draft')
},depends=['state'])
}, depends=['state'])
del _states_readonly
@classmethod
def __setup__(cls):
super(Invoice, cls).__setup__()
cls.payment_term.states['required'] = If(Bool(Eval('_parent_subtype.fe_document')) == True, True)
cls.payment_term.states['required'] = If(
Bool(Eval('_parent_subtype.fe_document')) == True, True)
cls._buttons.update({
'xml': {
'readonly': Eval('state').in_(['posted', 'paid']),
'depends' : ['state'],},
'depends': ['state'], },
'xml_signed': {
'readonly': Eval('state').in_(['posted', 'paid']),
'depends' : ['state'],},
'depends': ['state'], },
'fe_send': {
'readonly': Or(Eval('fe_delivery_state').in_(['done', 'exception']),
Eval('state').in_(['draft','validated'])),
'depends' : ['fe_delivery_state', 'state'],},
Eval('state').in_(['draft', 'validated'])),
'depends': ['fe_delivery_state', 'state'], },
'fe_update_status': {
'readonly': Eval('fe_delivery_state').in_(['done', 'exception']),
'depends' : ['fe_delivery_state'],},
'depends': ['fe_delivery_state'], },
'fe_email': {
'readonly': ~Eval('fe_delivery_state').in_(['done']),
'depends' : ['fe_delivery_state'],},
'depends': ['fe_delivery_state'], },
'post': {'pre_validate':
['OR',
('invoice_date', '!=', None),
('type', '!=', 'in'),
],
'invisible': (~Eval('state').in_(['draft', 'validated'])
| ((Eval('state') == 'posted') & Bool(Eval('move')))),
'readonly': If(Eval('total_amount') == 0,True),
'depends': ['state', 'move'],}
| ((Eval('state') == 'posted') & Bool(Eval('move')))),
'readonly': If(Eval('total_amount') == 0, True),
'depends': ['state', 'move'], }
})
@classmethod
def view_attributes(cls):
return super(Invoice, cls).view_attributes() + [
('//page[@id="fe_colombia_invoice"]', 'states', {
'invisible': ~Eval('fe_document', False)})]
return super(Invoice, cls).view_attributes() + [
('//page[@id="fe_colombia_invoice"]', 'states', {
'invisible': ~Eval('fe_document', False)})]
@classmethod
def trigger(cls, records, trigger):
@ -157,19 +157,17 @@ class Invoice(metaclass=PoolMeta):
cls.fe_send([record])
else:
record.fe_delivery_state = 'exception'
record.fe_delivery_status_description = 'DOCUMENTO NO ELECTRÓNICO'
record.fe_delivery_status_description = 'DOCUMENTO NO ELECTRÓNICO'
record.save()
@staticmethod
def default_fe_delivery_state():
return 'draft'
@staticmethod
def default_fe_states():
return None
@staticmethod
def default_fe_operation_type():
return '10'
@ -181,7 +179,7 @@ class Invoice(metaclass=PoolMeta):
@staticmethod
def default_fe_document():
return False
@classmethod
def copy(cls, invoices, default=None):
if default is None:
@ -210,10 +208,9 @@ class Invoice(metaclass=PoolMeta):
default.setdefault('fe_qrcode', None)
default.setdefault('fe_xml_file', None)
default.setdefault('fe_document_reference', None)
return super(Invoice, cls).copy(invoices, default=default)
@fields.depends('fe_qrcode')
def get_fe_qrcode_img(self, name):
qr_data = io.BytesIO()
@ -225,17 +222,17 @@ class Invoice(metaclass=PoolMeta):
qrcode_img.get_image().save(img, 'png')
img.seek(0)
return img.read()
def get_email_invoice_count(self, name):
pool = Pool()
Notification_Email = pool.get('notification.email')
Notification_Email_Log = pool.get('notification.email.log')
notification_email = Notification_Email.search(['rec_name', '=', 'Enviar Factura por correo'])
notification_email = Notification_Email.search(
['rec_name', '=', 'Enviar Factura por correo'])
domain = [('resource', '=', self)]
return str(Notification_Email_Log.search_count(domain))
def get_fe_states_icon(self, name):
if self.state == 'posted' or self.state == 'paid':
if self.fe_delivery_state != 'done':
@ -293,11 +290,11 @@ class Invoice(metaclass=PoolMeta):
def on_change_fe_operation_type(self):
if self.fe_operation_type == '10':
self.credit_note = False
@fields.depends('fe_operation_type')
def type_code_xml(self, facho_invoice):
if self.type == 'out':
if self.fe_operation_type == '10':
if self.fe_operation_type == '10':
xml = form_xml.DIANInvoiceXML(facho_invoice)
elif self.fe_operation_type == '20':
xml = form_xml.DIANCreditNoteXML(facho_invoice)
@ -308,24 +305,22 @@ class Invoice(metaclass=PoolMeta):
xml = form_xml.DIANSupportDocumentXML(facho_invoice)
elif self.fe_operation_type == '20':
xml = form_xml.DIANSupportDocumentCreditNoteXML(facho_invoice)
return xml
def fe_email_count(self, name):
pool = Pool()
Notification_Email_Log = pool.get('notification.email.log')
domain = [('resource', '=', self)]
return Notification_Email_Log.search_count(domain)
return Notification_Email_Log.search_count(domain)
@classmethod
def check_modify(cls, invoices):
'''
Check if the invoices can be modified
'''
return False
@classmethod
def validate(cls, invoices):
super().validate(invoices)
@ -335,19 +330,19 @@ class Invoice(metaclass=PoolMeta):
for taxes, unit_price, quantity, date_time in line.taxable_lines:
for tax in taxes:
if tax.type != 'percentage':
raise UserError('Solo se soporta impuesto tipo porcentaje para producto')
raise UserError(
'Solo se soporta impuesto tipo porcentaje para producto')
facho_invoice = invoice.tofacho()
xml = form_xml.DIANInvoiceXML(facho_invoice)
def tofacho(self):
if self.subtype.fe_document:
#Crear tipos de documentos electrónicos
if self.fe_operation_type in ['20','30']:
# Crear tipos de documentos electrónicos
if self.fe_operation_type in ['20', '30']:
reference = form.InvoiceDocumentReference(
ident = self.fe_document_reference.number,
uuid = self.fe_document_reference.fe_cufe,
date = self.fe_document_reference.invoice_date)
ident=self.fe_document_reference.number,
uuid=self.fe_document_reference.fe_cufe,
date=self.fe_document_reference.invoice_date)
operation_type = self.fe_operation_type
party_tax_code = self.party.identifiers[0].type
@ -363,67 +358,67 @@ class Invoice(metaclass=PoolMeta):
party_tax_code = '31'
if self.credit_note:
response = form.SupportDocumentCreditNoteResponse(
id = self.fe_document_reference.number,
code = '5',
description = self.description)
id=self.fe_document_reference.number,
code='5',
description=self.description)
inv = form.SupportDocumentCreditNote(reference, response)
operation_type = '10'
else:
inv = form.SupportDocument('05')
inv.set_period(datetime.now(tz=Bogota), datetime.now(tz=Bogota))
inv.set_issue(datetime.now(tz=Bogota))
if self.number:
inv.set_ident(self.number)
else:
inv.set_ident("0000")
#Adicionar tipos de operación
# Adicionar tipos de operación
inv.set_operation_type(operation_type)
company = form.Party(
legal_name = self.company.party.name,
name = self.company.party.full_name,
ident = form.PartyIdentification(
legal_name=self.company.party.name,
name=self.company.party.full_name,
ident=form.PartyIdentification(
str(self.company.party.identifiers[0].code),
str(self.company.party.identifiers[0].check_digit),
str(self.company.party.identifiers[0].type)
),
responsability_code = self.tax_level_code(self.company.party.tax_level_code),
responsability_regime_code = "48",
organization_code = self.party.type_person,
email = self.company.party.email,
address = form.Address('',
self.company.party.addresses[0].street,
form.City('05001',
self.company.party.addresses[0].city),
form.Country(self.company.party.addresses[0].country.code,
self.company.party.addresses[0].country.name),
form.CountrySubentity('05',
self.company.party.addresses[0].subdivision.name),
form.PostalZone(self.company.party.addresses[0].postal_code),
),
)
),
responsability_code=self.tax_level_code(self.company.party.tax_level_code),
responsability_regime_code="48",
organization_code=self.party.type_person,
email=self.company.party.email,
address=form.Address('',
self.company.party.addresses[0].street,
form.City('05001',
self.company.party.addresses[0].city),
form.Country(self.company.party.addresses[0].country.code,
self.company.party.addresses[0].country.name),
form.CountrySubentity('05',
self.company.party.addresses[0].subdivision.name),
form.PostalZone(self.company.party.addresses[0].postal_code),
),
)
party = form.Party(
legal_name = self.party.name,
name = self.party.full_name,
ident = form.PartyIdentification(
legal_name=self.party.name,
name=self.party.full_name,
ident=form.PartyIdentification(
str(self.party.identifiers[0].code),
str(self.party.identifiers[0].check_digit),
str(party_tax_code)
),
responsability_code = self.tax_level_code(self.party.tax_level_code),
responsability_regime_code = "48",
organization_code = self.party.type_person,
email = self.party.email,
address = form.Address('',
self.party.addresses[0].street,
form.City('05001', self.party.addresses[0].city),
form.Country(self.party.addresses[0].country.code,
self.party.addresses[0].country.name),
form.CountrySubentity('05', self.party.addresses[0].subdivision.name),
form.PostalZone(self.party.addresses[0].postal_code),
),
)
),
responsability_code=self.tax_level_code(self.party.tax_level_code),
responsability_regime_code="48",
organization_code=self.party.type_person,
email=self.party.email,
address=form.Address('',
self.party.addresses[0].street,
form.City('05001', self.party.addresses[0].city),
form.Country(self.party.addresses[0].country.code,
self.party.addresses[0].country.name),
form.CountrySubentity(
'05', self.party.addresses[0].subdivision.name),
form.PostalZone(self.party.addresses[0].postal_code),
),
)
if self.type == "in":
supplier = party
customer = company
@ -432,17 +427,15 @@ class Invoice(metaclass=PoolMeta):
supplier = company
customer = party
inv.set_supplier(supplier)
inv.set_customer(customer)
if self.payment_term:
payment_mean = form.PaymentMean(
id = self.fe_way_to_pay,
code = self.payment_term.payment_method.fe_payment,
due_at = datetime.now(tz=Bogota),
payment_id = '1'
id=self.fe_way_to_pay,
code=self.payment_term.payment_method.fe_payment,
due_at=datetime.now(tz=Bogota),
payment_id='1'
)
else:
raise UserError(str("Por Favor indique un plazo de pago"))
@ -450,13 +443,13 @@ class Invoice(metaclass=PoolMeta):
inv.set_payment_mean(payment_mean)
for line in self.lines:
inv.add_invoice_line(line.tofacho())
if line.type != 'title':
inv.add_invoice_line(line.tofacho())
inv.calculate()
return inv
def tax_level_code(self, tax_level):
tax_level_codes = ''
for codes in tax_level:
@ -465,7 +458,6 @@ class Invoice(metaclass=PoolMeta):
else:
tax_level_codes += ';' + codes.code
return tax_level_codes
@contextmanager
def acquire_public_key(config=None):
@ -493,36 +485,31 @@ class Invoice(metaclass=PoolMeta):
file_private_key.close()
file_certs.close()
def do_dian_request(self, request):
with self.acquire_public_key() as ctx:
client = dian.DianSignatureClient(ctx['file_private_key'],
ctx['file_public_key'],
ctx['passphrase'])
return client.request(request)
def _force_write(self, params, invoice):
params['fe_delivery_checked_at'] = datetime.now()
invoice.write([invoice], params)
def _dian_zip_io(self, filename, xml_invoice):
zipdata = io.BytesIO()
with fe.DianZIP(zipdata) as dianzip:
dianzip.add_invoice_xml(filename, xml_invoice)
zipdata.seek(0)
return zipdata
def _dian_xml_file_name(self, name):
m = hashlib.sha256()
m.update(name.encode('utf-8'))
filename = m.hexdigest()
return filename
def do_fe_delivery(self, facho_invoice, invoice):
config = Pool().get('account_invoice_facho.configuration')(1)
@ -530,7 +517,8 @@ class Invoice(metaclass=PoolMeta):
xml = self.type_code_xml(invoice, facho_invoice)
for extension in extensions:
xml.add_extension(extension)
fe_qrcode = xml.get_element_text('./ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts:DianExtensions/sts:QRCode')
fe_qrcode = xml.get_element_text(
'./ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts:DianExtensions/sts:QRCode')
fe_cufe = xml.get_element_text('./cbc:UUID')
with self.acquire_public_key(config) as ctx:
signer = fe.DianXMLExtensionSigner(ctx['file_certs'],
@ -539,8 +527,8 @@ class Invoice(metaclass=PoolMeta):
xml_signed = signer.sign_xml_string(str(xml))
req = dian.SendBillSync
filename = fe_cufe + '.xml'
filename = fe_cufe + '.xml'
filename_zip = self._dian_xml_file_name(self, fe_cufe) + '.zip'
zip_file = self._dian_zip_io(self, filename, xml_signed).read()
args = [filename_zip, zip_file]
@ -564,10 +552,10 @@ class Invoice(metaclass=PoolMeta):
delivery_trackid = res.ZipKey
status_description = "[]"
error_message = "[]"
#if res.StatusCode == '00':
#delivery_state = 'done'
#else:
#delivery_state = 'failure'
# if res.StatusCode == '00':
# delivery_state = 'done'
# else:
# delivery_state = 'failure'
else:
status_description = res.StatusDescription
error_message = res.ErrorMessage
@ -578,17 +566,17 @@ class Invoice(metaclass=PoolMeta):
self._force_write(self,
{'fe_delivery_state': delivery_state,
'fe_delivery_trackid': delivery_trackid,
'fe_delivery_status_description' : status_description,
'fe_delivery_error_message' : error_message,
'fe_qrcode' : fe_qrcode,
'fe_cufe' : fe_cufe,
'fe_xml_file' : zip_file
'fe_delivery_status_description': status_description,
'fe_delivery_error_message': error_message,
'fe_qrcode': fe_qrcode,
'fe_cufe': fe_cufe,
'fe_xml_file': zip_file
},
invoice)
def fe_process(self):
self.do_fe_delivery()
@classmethod
def fe_delivery(cls):
pool = Pool()
@ -600,12 +588,12 @@ class Invoice(metaclass=PoolMeta):
('subtype.sequence.invoice_resolution.valid_date_time_to',
'>=', date.today()),
('fe_delivery_state', 'not in', ['done', 'exeception'])]):
facho_invoice = inv.tofacho()
cls.do_fe_delivery(cls, facho_invoice, inv)
if inv.fe_delivery_state in ['delivered', 'failure'] and habilitation:
inv.fe_update_status([inv])
@fields.depends('credit_note')
def fe_extensions(self, inv):
pool = Pool()
@ -627,24 +615,23 @@ class Invoice(metaclass=PoolMeta):
invoice_resolution.technical_key,
ambiente,)
elif self.type == "in":
cufe = fe.DianXMLExtensionCUDS(inv,
cufe = fe.DianXMLExtensionCUDS(inv,
facho.dian_fe_pin,
ambiente,)
security_code = fe.DianXMLExtensionSoftwareSecurityCode(facho.dian_fe_software_identification,
facho.dian_fe_pin,
inv.invoice_ident)
authorization_provider = fe.DianXMLExtensionAuthorizationProvider()
#cufe = fe.DianXMLExtensionCUFE(inv,
#facho.dian_fe_invoice_resolution.technical_key,
#ambiente,
#)
# cufe = fe.DianXMLExtensionCUFE(inv,
# facho.dian_fe_invoice_resolution.technical_key,
# ambiente,
# )
nit = form.PartyIdentification(
str(facho.dian_fe_technologic_supplier.identifiers[0].code),
str(facho.dian_fe_technologic_supplier.identifiers[0].check_digit),
str(facho.dian_fe_technologic_supplier.identifiers[0].type)
)
)
software_provider = fe.DianXMLExtensionSoftwareProvider(nit,
nit.dv,
facho.dian_fe_software_identification)
@ -655,21 +642,19 @@ class Invoice(metaclass=PoolMeta):
invoice_resolution.prefix,
invoice_resolution.from_number,
invoice_resolution.to_number)
return [security_code, authorization_provider, cufe, software_provider, inv_authorization]
return [security_code, authorization_provider, cufe, software_provider, inv_authorization]
@classmethod
@ModelView.button
def xml(cls, invoices):
for invoice in invoices:
facho_invoice = invoice.tofacho()
xml = cls.type_code_xml(invoice, facho_invoice)
xml_encode = bytes(str(xml),'utf-8')
xml_encode = bytes(str(xml), 'utf-8')
zip_file = cls._dian_zip_io(cls, 'xml', str(xml)).read()
invoice.fe_xml_file = zip_file
invoice.save()
@classmethod
@ModelView.button
def xml_signed(cls, invoices):
@ -688,11 +673,10 @@ class Invoice(metaclass=PoolMeta):
passphrase=ctx['passphrase'],
localpolicy=True)
xml_signed = signer.sign_xml_string(xml_document)
xml_encode = bytes(str(xml_signed),'utf-8')
invoice.fe_xml_file = xml_encode
xml_encode = bytes(str(xml_signed), 'utf-8')
invoice.fe_xml_file = xml_encode
invoice.save()
@classmethod
@ModelView.button
def fe_send(cls, invoices):
@ -702,7 +686,6 @@ class Invoice(metaclass=PoolMeta):
cls.do_fe_delivery(cls, facho_invoice, invoice)
invoice.save()
@classmethod
@ModelView.button
def fe_update_status(self, invoices):
@ -715,7 +698,7 @@ class Invoice(metaclass=PoolMeta):
if config.dian_fe_habilitation:
req = dian.Habilitacion.GetStatusZip
resp = self.do_dian_request(self, req(trackId = invoice.fe_delivery_trackid))
resp = self.do_dian_request(self, req(trackId=invoice.fe_delivery_trackid))
params = {}
params['fe_delivery_status_description'] = resp.StatusDescription
params['fe_delivery_error_message'] = resp.ErrorMessage
@ -725,7 +708,6 @@ class Invoice(metaclass=PoolMeta):
params['fe_delivery_state'] = 'failure'
self._force_write(self, params, invoice)
@classmethod
@ModelView.button
def fe_email(cls, invoices, from_=None):
@ -737,11 +719,16 @@ class Invoice(metaclass=PoolMeta):
trigger = notification_email.triggers[0]
trigger.notification_email.send_email(invoices, trigger)
class PaymentTerm(metaclass=PoolMeta):
'Payment Term'
__name__ = 'account.invoice.payment_term'
payment_method = fields.Many2One('account.invoice.payment.method', "Payment Method", required=True)
payment_method = fields.Many2One(
'account.invoice.payment.method',
"Payment Method",
required=True)
class PaymentMethod(metaclass=PoolMeta):
'Payment Method'
@ -752,15 +739,16 @@ class PaymentMethod(metaclass=PoolMeta):
[
('10', 'Efectivo'),
('20', 'Cheque'),
('30','Transferencia Crédito'),
('31','Transferencia Débito'),
('30', 'Transferencia Crédito'),
('31', 'Transferencia Débito'),
('42', 'Consignación Bancaria')
],"Payment Fe",states={
'readonly': If(Bool(Eval('fe_method')) == False, True),
'required': If(Bool(Eval('fe_method')) == True, True)
], "Payment Fe", states={
'readonly': If(Bool(Eval('fe_method')) == False, True),
'required': If(Bool(Eval('fe_method')) == True, True)
}
)
class Product(metaclass=PoolMeta):
__name__ = 'product.product'
@ -768,14 +756,14 @@ class Product(metaclass=PoolMeta):
code = "001"
if self.code:
code = self.code
return form.StandardItem(
description = self.name,
id_ = code,
name = 'Estándar de adopción del contribuyente'
description=self.name,
id_=code,
name='Estándar de adopción del contribuyente'
)
class InvoiceLine(metaclass=PoolMeta):
__name__ = 'account.invoice.line'
@ -784,43 +772,49 @@ class InvoiceLine(metaclass=PoolMeta):
super(InvoiceLine, cls).__setup__()
cls.product.states['required'] = True
cls.unit_price.domain = [('unit_price', '!=', 0)]
def _get_price_type(self, line_tax):
if line_tax.subtotals:
tax, = line_tax.subtotals
tax_scheme = tax.scheme.name
return tax_scheme
def tofacho(self):
tax_subtotals = []
line_tax = form.TaxTotalOmit()
line_withholding = form.WithholdingTaxTotalOmit()
for taxes, unit_price, quantity, date_time in self.taxable_lines:
if len(taxes) != 0:
for tax in taxes:
line_percent = tax.rate * 100
if line_percent > 0:
line_tax = form.TaxTotal(
subtotals = [
subtotals=[
form.TaxSubTotal(
percent = abs(line_percent),
percent=abs(line_percent),
scheme=form.TaxScheme(tax.fe_tax_type))
])
elif line_percent < 0:
line_withholding = form.WithholdingTaxTotal(
subtotals = [
subtotals=[
form.WithholdingTaxSubTotal(
percent = abs(line_percent),
percent=abs(line_percent),
scheme=form.TaxScheme(tax.fe_tax_type))
])
return form.InvoiceLine(
quantity = form.Quantity(abs(self.quantity), '94'),
description = self.description,
item = self.product.tofacho(),
price = form.Price(
amount = form.Amount(abs(self.unit_price)),
type_code = '01',
type = 'IVA'
),
tax = line_tax,
withholding = line_withholding)
quantity=form.Quantity(abs(self.quantity), '94'),
description=self.description,
item=self.product.tofacho(),
price=form.Price(
amount=form.Amount(abs(self.unit_price)),
type_code='01',
type=self._get_price_type(line_tax)
),
tax=line_tax,
withholding=line_withholding)
class InvoiceReportDianZip(Report):
@ -840,25 +834,26 @@ class InvoiceReportDianZip(Report):
invoice, = Invoice.browse(records)
invoice.invoice_report_cache = None
invoice.save()
Report = pool.get('account.invoice', type='report')
Report = pool.get('account.invoice', type='report')
ext, content, _, name = Report.execute([invoice.id], {})
if not invoice.fe_cufe:
raise UserError(str('Factura no enviada a la Dian'))
zip_file = io.BytesIO(invoice.fe_xml_file)
name = invoice._dian_xml_file_name(invoice.fe_cufe + '.xml')
filename = name + '.' + ext
filename = name + '.' + ext
with zipfile.ZipFile(zip_file, 'a') as pdf_add:
pdf_add.writestr(filename, content)
zip_file.seek(0)
return ('zip', zip_file.read(), False, name)
class InvoiceSubtype(metaclass=PoolMeta):
'Invoice Subtype'
__name__ = 'account.invoice.subtype'
fe_document = fields.Boolean('Fe Document')
class SendInvoice(Wizard):
'Send Invoices'
__name__ = 'account.invoice.send_invoices'
@ -876,4 +871,6 @@ class SendInvoice(Wizard):
Invoice.fe_send([record])
elif record.state == 'posted' and record.fe_delivery_state == 'exception' or record.fe_delivery_state == 'done':
continue
1