diff --git a/source/app/models/main.py b/source/app/models/main.py index b1f42ab..25f8353 100644 --- a/source/app/models/main.py +++ b/source/app/models/main.py @@ -41,6 +41,7 @@ from settings import log, COMPANIES, VERSION, PATH_CP, PRE, CURRENT_CFDI, \ from controllers import utils from settings import ( DEBUG, + DEFAULT_GLOBAL, DB_COMPANIES, EXT, IS_MV, @@ -57,7 +58,7 @@ FORMAT4 = '{0:.4f}' FORMAT6 = '{0:.6f}' FORMAT_TAX = FORMAT4 FORMAT_PRECIO = FORMAT4 -RFC_PUBLICO = 'XAXX010101000' +# ~ RFC_PUBLICO = 'XAXX010101000' RFC_EXTRANJERO = 'XEXX010101000' @@ -4242,7 +4243,7 @@ class Facturas(BaseModel): if invoice.donativo and invoice.forma_pago == '12': return - if invoice.cliente.rfc == RFC_PUBLICO: + if invoice.cliente.rfc == RFCS['PUBLIC']: return importe = invoice.total_mn @@ -4804,6 +4805,9 @@ class Facturas(BaseModel): def _make_xml(self, invoice, auth): tax_decimals = Configuracion.get_bool('chk_config_tax_decimals') decimales_precios = Configuracion.get_bool('chk_config_decimales_precios') + invoice_by_ticket = Configuracion.get_bool('chk_config_invoice_by_ticket') + is_global = (invoice.cliente.rfc == RFCS['PUBLIC']) and invoice_by_ticket + frm_vu = FORMAT if decimales_precios: frm_vu = FORMAT_PRECIO @@ -4869,7 +4873,6 @@ class Facturas(BaseModel): 'Nombre': emisor.nombre, 'RegimenFiscal': invoice.regimen_fiscal, } - receptor = { 'Rfc': invoice.cliente.rfc, 'Nombre': invoice.cliente.nombre, @@ -4884,9 +4887,15 @@ class Facturas(BaseModel): conceptos = [] rows = FacturasDetalle.select().where(FacturasDetalle.factura==invoice) for row in rows: + if is_global: + key_sat = row.clave_sat + key = row.clave + else: + key_sat = row.producto.clave_sat + key = row.producto.clave concepto = { - 'ClaveProdServ': row.producto.clave_sat, - 'NoIdentificacion': row.producto.clave, + 'ClaveProdServ': key_sat, + 'NoIdentificacion': key, 'Cantidad': FORMAT.format(row.cantidad), 'ClaveUnidad': row.unidad, 'Unidad': SATUnidades.get(SATUnidades.key==row.unidad).name[:20], @@ -4918,7 +4927,21 @@ class Facturas(BaseModel): retenciones = [] if invoice.tipo_comprobante != 'T': - for impuesto in row.producto.impuestos: + + if is_global: + ticket = (Tickets + .get(fn.Concat(Tickets.serie, Tickets.folio)==row.clave) + ) + product_taxes = (TicketsImpuestos + .select() + .where(TicketsImpuestos.ticket==ticket) + ) + else: + product_taxes = row.producto.impuestos + + for impuesto in product_taxes: + if is_global: + impuesto = impuesto.impuesto base = float(row.importe - row.descuento) if impuesto.tipo == 'E': tax = { @@ -5198,7 +5221,7 @@ class Facturas(BaseModel): tipo_persona = 1 if receptor['rfc'] == 'XEXX010101000': tipo_persona = 4 - elif receptor['rfc'] == RFC_PUBLICO: + elif receptor['rfc'] == RFCS['PUBLIC']: tipo_persona = 3 elif len(receptor['rfc']) == 12: tipo_persona = 2 @@ -5302,7 +5325,7 @@ class Facturas(BaseModel): tipo_persona = 1 if receptor['rfc'] == 'XEXX010101000': tipo_persona = 4 - elif receptor['rfc'] == RFC_PUBLICO: + elif receptor['rfc'] == RFCS['PUBLIC']: tipo_persona = 3 elif len(receptor['rfc']) == 12: tipo_persona = 2 @@ -5455,7 +5478,7 @@ class Facturas(BaseModel): if invoice.donativo and invoice.forma_pago == '12': return - if invoice.cliente.rfc == RFC_PUBLICO: + if invoice.cliente.rfc == RFCS['PUBLIC']: return importe = invoice.total_mn @@ -7232,15 +7255,85 @@ class Tickets(BaseModel): } return data + def _get_totals_invoice_by_ticket(self, invoice, ids): + subtotal = 0 + descuento_cfdi = 0 + totals_tax = {} + total_trasladados = None + total_retenciones = None + notes = Tickets.get_notes(ids) + + rows = Tickets.select().where(Tickets.id.in_(ids)) + + for row in rows: + details = DEFAULT_GLOBAL.copy() + details['clave'] = row.serie + str(row.folio) + details['factura'] = invoice.id + + unit_value = row.subtotal + discount = row.descuento + final_price = unit_value - discount + importe = final_price + + details['valor_unitario'] = unit_value + details['descuento'] = discount + details['precio_final'] = final_price + details['importe'] = importe + + descuento_cfdi += details['descuento'] + subtotal += details['importe'] + + FacturasDetalle.create(**details) + + rows = (TicketsImpuestos + .select( + TicketsImpuestos.impuesto, + fn.Sum(TicketsImpuestos.base), + fn.Sum(TicketsImpuestos.importe)) + .where(TicketsImpuestos.ticket.in_(ids)) + .group_by(TicketsImpuestos.impuesto) + .order_by(TicketsImpuestos.impuesto) + ) + for tax in rows: + invoice_tax = { + 'factura': invoice.id, + 'impuesto': tax.impuesto.id, + 'base': tax.base, + 'importe': tax.importe, + } + FacturasImpuestos.create(**invoice_tax) + + if tax.impuesto.tipo == 'T' and tax.impuesto.key != '000': + total_trasladados = (total_trasladados or 0) + tax.importe + elif tax.impuesto.tipo == 'R' and tax.impuesto.key != '000': + total_retenciones = (total_retenciones or 0) + tax.importe + + total = subtotal - descuento_cfdi + \ + (total_trasladados or 0) - (total_retenciones or 0) + + type_change = Decimal(invoice.tipo_cambio) + total_mn = round(total * type_change, DECIMALES) + data = { + 'subtotal': subtotal, + 'descuento': descuento_cfdi, + 'total': total, + 'total_mn': total_mn, + 'total_trasladados': total_trasladados, + 'total_retenciones': total_retenciones, + 'notas': notes, + } + return data + @classmethod def invoice(cls, values, user): is_invoice_day = util.get_bool(values['is_invoice_day']) id_client = int(values['client']) tickets = util.loads(values['tickets']) + invoice_by_ticket = Configuracion.get_bool('chk_config_invoice_by_ticket') if is_invoice_day: filters = ( - Socios.rfc == RFC_PUBLICO and + Socios.rfc == RFCS['PUBLIC'] and Socios.slug == 'publico_en_general') try: client = Socios.get(filters) @@ -7275,7 +7368,10 @@ class Tickets(BaseModel): with database_proxy.atomic() as txn: obj = Facturas.create(**data) - totals = cls._calculate_totals_invoice(cls, obj, tickets) + if is_invoice_day and invoice_by_ticket: + totals = cls._get_totals_invoice_by_ticket(cls, obj, tickets) + else: + totals = cls._calculate_totals_invoice(cls, obj, tickets) obj.subtotal = totals['subtotal'] obj.descuento = totals['descuento'] obj.total_trasladados = totals['total_trasladados'] @@ -7447,6 +7543,7 @@ class Tickets(BaseModel): data['timbre'] = {} data['donataria'] = {} data['ine'] = {} + data['leyendas'] = () return data @@ -9183,7 +9280,7 @@ def _init_values(rfc): data = ( {'clave': 'version', 'valor': VERSION}, {'clave': 'migracion', 'valor': '0'}, - {'clave': 'rfc_publico', 'valor': RFC_PUBLICO}, + {'clave': 'rfc_publico', 'valor': RFCS['PUBLIC']}, {'clave': 'rfc_extranjero', 'valor': 'XEXX010101000'}, {'clave': 'decimales', 'valor': '2'}, {'clave': 'path_key', 'valor': ''}, diff --git a/source/app/settings.py b/source/app/settings.py index 3476f67..1b5bfdc 100644 --- a/source/app/settings.py +++ b/source/app/settings.py @@ -236,8 +236,16 @@ VALUES_PDF = { RFCS = { 'PUBLIC': 'XAXX010101000', + 'FOREIGN': 'XEXX010101000', } URL = { 'SEAFILE': 'https://seafile.elmau.net', } + +DEFAULT_GLOBAL = { + 'cantidad': 1.00, + 'unidad': 'ACT', + 'descripcion': 'Venta', + 'clave_sat': '01010101', +} diff --git a/source/templates/plantilla_factura.ods b/source/templates/plantilla_factura.ods index a35d18a..0505175 100644 Binary files a/source/templates/plantilla_factura.ods and b/source/templates/plantilla_factura.ods differ