File size: 9,481 Bytes
89271ce
 
 
8ddc385
 
ec7b947
 
 
89271ce
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ec7b947
 
 
 
89271ce
ec7b947
 
 
89271ce
ec7b947
 
89271ce
 
ec7b947
c1be086
 
 
89271ce
 
 
 
 
c1be086
 
89271ce
 
ad87dba
 
 
89271ce
461a71e
c1be086
8ddc385
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
import gradio as gr
import pandas as pd
import tempfile
import os
import uuid
import shutil

os.makedirs("outputs", exist_ok=True)  # Carpeta pública para los resultados

def procesar_bases(file1, file2):
    try:
        # Leer archivos
        dff = pd.read_excel(file1, engine='openpyxl')
        dfp = pd.read_excel(file2, engine='openpyxl')

# ---  Existing code from the provided script starts here ---
        # FASE 1: FACTURAS
        dff_expanded=dff.copy()
        dff_expanded[['FORMA DE PAGO','CANAL','MES','AÑO','UNIDADES']]=None

        dff_expanded=dff_expanded[['ID', 'FORMA DE PAGO','COMPANIA','CANAL', 'RFC', 'RAZON SOCIAL', 'CP', 'CORREO ELECTRONICO',
                                    'REGIMEN FISCAL', 'METODO DE PAGO', 'FECHA PAGO', 'USO CFDI',
                                    'ES RESICO', 'FECHA FACT.', 'MES', 'AÑO', 'OBSERVACIONES', 'EJECUTIVO ID SOl',
                                    'EJECUTIVO SOL', 'EJECUTIVO ID EMITE', 'EJECUTIVO EMITE',
                                    'EJECUTIVO FECHA EMITE', 'SUBTOTAL', 'RETENCIONES', 'DESCUENTO', 'IVA',
                                    'TOTAL', 'UUID', 'UUID REL', 'ESTATUS', 'SERIE', 'FOLIO',
                                    'UNIDADES','FOLIO COMPLEMENTO']]

       # 2.- Agrego la columna CANAL
        dff_expanded['CANAL'] = 'DIRECTO'  # Valor por defecto
        dff_expanded.loc[dff_expanded['SERIE'] == 'AFM', 'CANAL'] = 'ASEGURADORA'

        dff_expanded['FECHA FACT.'] = pd.to_datetime(dff_expanded['FECHA FACT.'], format='%d/%m/%Y', errors='coerce')
        # Extract the year and month
        dff_expanded['AÑO'] = dff_expanded['FECHA FACT.'].dt.year
        dff_expanded['MES'] = dff_expanded['FECHA FACT.'].dt.month

        # FASE 2: PARTIDAS
        #1.- Extraemos la información de la columna "CONCEPTO"
        def split_concepto(row):
            parts = row['CONCEPTO'].split(':')
            return parts

        # Aplicar la función a cada fila del DataFrame
        dfp_expanded = dfp.copy()  # Crear una copia para evitar modificar el original
        dfp_expanded['concepto_split'] = dfp.apply(split_concepto, axis=1)

        # Obtener el máximo número de partes para crear las nuevas columnas
        max_parts = dfp_expanded['concepto_split'].apply(len).max()

        # Crear nuevas columnas para cada parte del concepto
        for i in range(max_parts):
            dfp_expanded[f'concepto_{i+1}'] = dfp_expanded['concepto_split'].apply(lambda x: x[i] if len(x) > i else None)


        # Eliminar la columna 'concepto_split'
        dfp_expanded = dfp_expanded.drop('concepto_split', axis=1)

        #- Quitamos las columnas que nos estorban y les cambiamos el nombre a las que nos funcionan
        dfp_expanded_final=dfp_expanded[['ID FACTURA','concepto_2','concepto_5','concepto_8','concepto_11','concepto_13','concepto_14','ORDEN ASOCIADA', 'ORDEN ASOCIADA ID',
                                        'ORDEN ASOCIADA EJEC ID', 'ORDEN ASOCIADA EJECUTIVO','ORDEN ASOCIADA NEGOCIO', 'ORDEN ASOCIADA TIPO NEGOCIO','ORDEN ASOCIADA ESTATUS', 'CUENTA CONTABLE', 'CODIGO PRODUCTO',
                                         'CANTIDAD', 'MEDIDA', 'CODIGO UNIDAD', 'PRECIO UNITARIO', 'PARCIAL']]

        dfp_expanded_final = dfp_expanded_final.rename(columns={'concepto_2': 'VIN', 'concepto_5':'RECIBO','concepto_8':'SOLUCION','concepto_11':'CONCEPTO','concepto_13':'PERIODO SERVICIO','concepto_14':'ULT SERVICIO CUBIERTO'})

        empty_concepto_2 = dfp_expanded_final[(dfp_expanded_final['VIN'].isnull()) | (dfp_expanded_final['VIN'] == '')]
        # 3.- Quitamos esas facturas de la tabla que vamos a manipular
        dfp_expanded_final = dfp_expanded_final[dfp_expanded_final['VIN'].notnull()]
        #Quito los espacios en los valores de la columna RECIBO
        dfp_expanded_final['RECIBO'] = dfp_expanded_final['RECIBO'].str.strip()
        dfp_expanded_final['FORMA DE PAGO'] = 'MENSUAL'  # Default value
        dfp_expanded_final.loc[dfp_expanded_final['RECIBO'] == '1/1', 'FORMA DE PAGO'] = 'CONTADO'

        dfp_expanded_final['ORDEN NEGOCIO'] = 'OTRO' # Valor por defecto
        dfp_expanded_final.loc[dfp_expanded_final['ORDEN ASOCIADA NEGOCIO'] == 'MOVIMIENTO', 'ORDEN NEGOCIO'] = 'MOVIMIENTO'
        dfp_expanded_final.loc[dfp_expanded_final['ORDEN ASOCIADA NEGOCIO'].isin(['NUEVO', 'RECUPERADO NUEVO']), 'ORDEN NEGOCIO'] = 'NUEVO'
        dfp_expanded_final.loc[dfp_expanded_final['ORDEN ASOCIADA NEGOCIO'].isin(['RENOVACIÓN','RENOVACION','RECUPERADO RENOVACIÓN','RECUPERADO RENOVACION','RENOVACI�N']), 'ORDEN NEGOCIO'] = 'RENOVACION'

        dfp_expanded_final=pd.merge(dfp_expanded_final,dff_expanded[['ID','CANAL','FECHA FACT.','MES', 'AÑO','COMPANIA','RAZON SOCIAL','FOLIO']],left_on='ID FACTURA',right_on='ID',how='left')

        dfp_expanded_final=dfp_expanded_final[['ID FACTURA','CANAL','FORMA DE PAGO', 'FECHA FACT.','COMPANIA','FOLIO','RAZON SOCIAL','VIN', 'RECIBO', 'SOLUCION', 'CONCEPTO',
                                              'PERIODO SERVICIO', 'ULT SERVICIO CUBIERTO', 'ORDEN ASOCIADA','ORDEN ASOCIADA ID', 'ORDEN ASOCIADA EJEC ID','ORDEN ASOCIADA EJECUTIVO','ORDEN NEGOCIO','ORDEN ASOCIADA NEGOCIO',
                                              'ORDEN ASOCIADA TIPO NEGOCIO', 'ORDEN ASOCIADA ESTATUS','CUENTA CONTABLE', 'CODIGO PRODUCTO', 'CANTIDAD', 'MEDIDA','CODIGO UNIDAD', 'PRECIO UNITARIO', 'PARCIAL','MES', 'AÑO']]

        #FASE 3: FACTURAS
        dff_expanded = dff_expanded.drop(columns=['FORMA DE PAGO'])
        # Group by 'ID FACTURA' and get the unique payment methods
        payment_methods_per_invoice = dfp_expanded_final.groupby('ID FACTURA')['FORMA DE PAGO'].unique()

        # Create a new DataFrame with 'ID FACTURA' and the preferred payment method
        facturas_forma_pago = pd.DataFrame({
            'ID FACTURA': payment_methods_per_invoice.index,
            'FORMA DE PAGO': payment_methods_per_invoice.values})

        # Function to select the preferred payment method
        def select_payment_method(payment_methods):
            if 'MENSUAL' in payment_methods:
                return 'MENSUAL'
            else:
                return payment_methods[0]  # Return the first payment method if 'MENSUAL' is not present


        # Apply the function to select the preferred payment method
        facturas_forma_pago['FORMA DE PAGO'] = facturas_forma_pago['FORMA DE PAGO'].apply(select_payment_method)

        # Perform the vlookup operation
        dff_expanded = pd.merge(dff_expanded, facturas_forma_pago[['ID FACTURA', 'FORMA DE PAGO']], left_on='ID', right_on='ID FACTURA', how='left')
        # Fill NaN values in 'FORMA DE PAGO' with 'CONTADO'
        dff_expanded['FORMA DE PAGO'] = dff_expanded['FORMA DE PAGO'].fillna('CONTADO')

        base_unidades = dfp_expanded_final['CANTIDAD'].groupby(dfp_expanded_final['ID FACTURA']).sum()
        dff_expanded = dff_expanded.drop(columns=['UNIDADES'])
        dff_expanded['UNIDADES']=pd.merge(dff_expanded,base_unidades.reset_index(),left_on='ID',right_on='ID FACTURA',how='left')['CANTIDAD']
        # Fill NaN values in 'UNIDADES' column with 0
        dff_expanded['UNIDADES'] = dff_expanded['UNIDADES'].fillna(0)

        dff_expanded_final=dff_expanded[['ID','FORMA DE PAGO','COMPANIA', 'CANAL', 'RFC', 'RAZON SOCIAL', 'CP',
                                          'CORREO ELECTRONICO', 'REGIMEN FISCAL', 'METODO DE PAGO', 'FECHA PAGO',
                                          'USO CFDI', 'ES RESICO', 'FECHA FACT.', 'MES', 'AÑO', 'OBSERVACIONES',
                                          'EJECUTIVO ID SOl', 'EJECUTIVO SOL', 'EJECUTIVO ID EMITE',
                                          'EJECUTIVO EMITE', 'EJECUTIVO FECHA EMITE', 'SUBTOTAL', 'RETENCIONES',
                                          'DESCUENTO', 'IVA', 'TOTAL', 'UUID', 'UUID REL', 'ESTATUS', 'SERIE',
                                          'FOLIO','UNIDADES','FOLIO COMPLEMENTO']]



        # --- Existing code ends here ---
        uid = uuid.uuid4().hex
        output1_path = f"outputs/facturas_limpias_{uid}.xlsx"
        output2_path = f"outputs/partidas_limpias_{uid}.xlsx"
        output3_path = f"outputs/partidas_manuales_{uid}.xlsx"

        dff_expanded_final.to_excel(output1_path, index=False, engine='openpyxl')
        dfp_expanded_final.to_excel(output2_path, index=False, engine='openpyxl')
        empty_concepto_2.to_excel(output3_path, index=False, engine='openpyxl')

        # Devolver rutas relativas (Gradio las convertirá en URLs públicas si usas static_dir="outputs")
        return output1_path, output2_path, output3_path

    except Exception as e:
        error_file = f"outputs/error_{uuid.uuid4().hex}.txt"
        with open(error_file, "w") as f:
            f.write("Error:\n" + str(e))
        return error_file, None, None

# Interfaz Gradio
gr.Interface(
    fn=procesar_bases,
    inputs=[
        gr.File(label="Sube en formato Excel la base Facturas", type="filepath"),
        gr.File(label="Sube en formato Excel la base Partidas", type="filepath")
    ],
    outputs=[
        gr.File(label="Base Facturas Limpias"),
        gr.File(label="Base Partidas Limpias"),
        gr.File(label="Base Partidas Manuales")
    ],
    title="Limpieza de la base Facturas y Partidas",
    description="Instrucciones: Sube dos archivos de Excel. El primero debe ser la base de 'Facturas', el segundo la base de 'Partidas'. El sistema devuelve tres archivos procesados."
).launch()