-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcontabilidad.py
381 lines (280 loc) · 13.6 KB
/
contabilidad.py
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
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
from imports import *
from constantes import *
from funciones import *
class Asiento():
'''Un asiento es la unidad que compone el libro diario'''
def __init__(self, concepto:str, debe:dict, haber:dict, \
fecha=dt.date.today()):
self.concepto = concepto
self.debe = debe # Diccionario de pares cuenta:int : cantidad:float
self.haber = haber # Diccionario de pares cuenta:int : cantidad:float
self.fecha = fecha
def tabla(self) -> pd.DataFrame:
# Esta función convierte el asiento en una Pandas DataFrame
d = np.full([len(self.debe)+len(self.haber),6], None)
for i, codigo in enumerate(self.debe):
d[i, 0] = self.fecha
d[i, 1] = self.concepto
d[i, 2] = codigo
d[i, 3] = CUENTAS[codigo]
d[i, 4] = self.debe[codigo]
for i, codigo in enumerate(self.haber):
i_ = len(self.debe)
d[i_+i, 0] = self.fecha
d[i_+i, 1] = self.concepto
d[i_+i, 2] = codigo
d[i_+i, 3] = CUENTAS[codigo]
d[i_+i, 5] = self.haber[codigo]
tabla = pd.DataFrame(d, columns=['Fecha', 'Concepto','Cuenta', 'Nombre de cuenta', 'Debe', 'Haber'])
return tabla
# funciones estéticas
def __str__(self) -> str:
representacion = f"\n\n'{self.fecha}'\t\t{self.concepto}\n"
representacion += "-"*64 + "\n"
columna_debe = ""
for key in self.debe:
columna_debe += f"\t{self.debe[key]}€\t{key}\t{CUENTAS[key]}\n"
columna_haber = ""
for key in self.haber:
columna_haber += f"\t{self.haber[key]}€\t{key}\t{CUENTAS[key]}\n"
representacion += "\nDEBE\n" +columna_debe + "\nHABER\n"+columna_haber+"\n"
return representacion
def __repr__(self) -> str:
representacion = f"\n\n'{self.fecha}'\t\t{self.concepto}\n"
representacion += "-"*64 + "\n"
columna_debe = ""
for key in self.debe:
columna_debe += f"\t{self.debe[key]}€\t{key}\t{CUENTAS[key]}\n"
columna_haber = ""
for key in self.haber:
columna_haber += f"\t{self.haber[key]}€\t{key}\t{CUENTAS[key]}\n"
representacion += "\nDEBE\n"+columna_debe+"\nHABER\n"+columna_haber+"\n"
return representacion
class LibroDiario(list):
'''
Un LibroDiario es una lista de asientos
'''
def tabla(self) -> pd.DataFrame:
'''
Devuelve un Pandas DataFrame con todos los asientos
'''
tabla = self[0].tabla()
tabla.insert(0,'Número de asiento', [0]*(len(self[0].debe)+len(self[0].haber)))
for numero_de_asiento, asiento in enumerate(self):
if asiento != self[0]:
asiento_tabla = asiento.tabla()
asiento_tabla.insert(0,'Número de asiento', [numero_de_asiento]*(len(asiento.debe)+len(asiento.haber)))
tabla = pd.concat([tabla,asiento_tabla], ignore_index=True)
# tabla = tabla.sort_values(by=['Fecha'], ignore_index = True)
return tabla
# Funciones para almacenamiento de información en .csv
def save_csv(self, name) -> None:
self.tabla().to_csv(name + '.csv', index=False)
pass
def load_csv(self, name) -> None:
df = pd.read_csv(name+'.csv')
asiento_N = df['Número de asiento'].iloc[-1]
for asiento_n in range(asiento_N+1):
asiento_tabla = df[(df['Número de asiento'] == asiento_n)]
concepto = asiento_tabla.iloc[0]['Concepto']
fecha = asiento_tabla.iloc[0]['Fecha']
debe = {}
haber = {}
for index, row in asiento_tabla.iterrows():
cuenta = int(row['Cuenta'])
if pd.isna(row['Haber']):
# Es de debe
debe[cuenta] = int(row['Debe'])
if pd.isna(row['Debe']):
# Es de haber
haber[cuenta] = int(row['Haber'])
asiento = Asiento(concepto, debe, haber, fecha)
self.append(asiento)
# modificación del método .append
def append(self, asiento:Asiento) -> None:
if not isinstance(asiento, Asiento):
raise RuntimeError("Solo pueden añadirse asientos al libro diario")
elif not(sum(asiento.debe.values()) == sum(asiento.haber.values())):
raise RuntimeError("El asiento no es correcto")
else:
super().append(asiento)
# función estética
def mostrar_por_dias(self) -> None:
self.fechas = []
for asiento in self:
if not(asiento.fecha in self.fechas): self.fechas.append(asiento.fecha)
for fecha in self.fechas:
asientos_en_fecha = [asiento for asiento in self if asiento.fecha == fecha]
tabla = asientos_en_fecha[0].tabla()
for asiento in asientos_en_fecha:
if asiento != asientos_en_fecha[0]:
tabla = pd.concat([tabla, asiento.tabla()], ignore_index=True)
print(f"Asientos a {fecha}")
display(tabla)
class LibroMayor(dict):
'''
Un LibroMayor es un diccionario que se crea a partir de un LibroDiario y
tiene la estructura:
cuenta:int : d:DataFrame
'''
def __init__(self, libro_diario:LibroDiario):
self.libro_diario = libro_diario # libro diario del que se generó
self.cuentas = [] # lista con todas las cuentas que aparecen, ordenadas
self.totales = {} # diccionario con los pares cuenta:int : total:float
# Anadir todas las cuentas de todos los asientos del libro a la lista
# self.cuentas y ordenarlas
for asiento in self.libro_diario:
for cuenta in asiento.debe:
if not(cuenta in self.cuentas): self.cuentas.append(cuenta)
for cuenta in asiento.haber:
if not(cuenta in self.cuentas): self.cuentas.append(cuenta)
self.cuentas.sort()
# Generar el diccionario siguiendo el formato:
# cuenta:int : d:DataFrame
for cuenta in self.cuentas:
d = libro_diario.tabla()
d = d[d["Cuenta"] == cuenta]
self[cuenta] = d
self.calcular_totales()
def calcular_totales(self) -> None:
'''Calcular el diccionario self.totales'''
for cuenta in self.cuentas:
self.totales[cuenta] = self[cuenta]["Debe"]\
.sum()-self[cuenta]["Haber"].sum()
# La cuenta será positiva si es deudora y negativa si es acreedora.
def sumar_cuentas(self,lista_de_cuentas:list):
'''función que permite sumar cuentas de una lista de cuentas
permite valores positivos y negativos'''
total = 0
for cuenta in lista_de_cuentas:
if cuenta in self.totales:
if cuenta>0: total+=self.totales[abs(cuenta)]
if cuenta<0: total-=self.totales[abs(cuenta)]
return total
def __repr__(self):
self.calcular_totales()
for cuenta in self:
print(f"\n{cuenta}:\t{CUENTAS[cuenta]}")
display(self[cuenta][["Concepto", "Debe", "Haber"]])
if self.totales[cuenta]>0:
print(f"cuenta deudora: {self.totales[cuenta]}")
if self.totales[cuenta]<0:
print(f"cuenta acreedora: {-self.totales[cuenta]}")
return " "
class PerdidasYGanancias():
def __init__(self, libro_diario:LibroDiario, libro_mayor:LibroMayor):
self.libro_diario = libro_diario
self.libro_mayor = libro_mayor
# Se genera .grupos replicando el diccionario PYG_GRUPOS
self.grupos = copy.deepcopy(PYG_GRUPOS)
# Calcular los valores del diccionario .grupos y sustituir las listas
for grupo in PYG_GRUPOS:
if type(PYG_GRUPOS[grupo]) is list:
#asiento = Asiento("Regularización de pérdidas y ganancias", ...)
suma = self.libro_mayor.sumar_cuentas(PYG_GRUPOS[grupo]) # La suma correspondiente
self.grupos[grupo] = suma
else:
total_grupo = 0
for subgrupo in PYG_GRUPOS[grupo]:
if type(PYG_GRUPOS[grupo][subgrupo]) is list:
suma = self.libro_mayor.sumar_cuentas(PYG_GRUPOS[grupo][subgrupo]) # La suma correspondiente
self.grupos[grupo][subgrupo] = suma
else:
for subsubgrupo in PYG_GRUPOS[grupo][subgrupo]:
suma = self.libro_mayor.sumar_cuentas(PYG_GRUPOS[grupo][subgrupo][subsubgrupo]) # La suma correspondiente
self.grupos[grupo][subgrupo][subsubgrupo] = suma
# Calcular el diccionario .totales
self.totales = copy.deepcopy(PYG_TOTALES)
for total in PYG_TOTALES:
suma = 0
for grupo_n in PYG_TOTALES[total]:
suma += sumar_ultimos_niveles(list(self.grupos.values())[grupo_n-1])
self.totales[total] = suma
# Definir la cuenta de pérdidas y ganancias
# Asientos de regularización
self.asientos = []
def __repr__(self):
return str(list(self.totales.values())[-1])
def __float__(self):
return float(list(self.totales.values())[-1])
class Balance(dict):
'''
Un balance es un diccionario de diccionarios y almacena las cuentas de cada
sección.
La información tiene estructura de árbol.
SECCION>SUBSECCIÓN>SUBSUBSECCIÓN>total:float
En el último nivel se encuentra el total de la cuenta
'''
# (Queda un último nivel en el arbol de las secciones que he agrupado en sus
# cuentas padres por simplicidad, queda por hacer).
def __init__(self, libro:LibroMayor):
# Se genera replicando el diccionario ejemplo BALANCE.
self["Activo"] = copy.deepcopy(BALANCE["Activo"])
self["Patrimonio neto"] = copy.deepcopy(BALANCE["Patrimonio neto"])
self["Pasivo"] = copy.deepcopy(BALANCE["Pasivo"])
# self.totales permite acceder a los totales de las secciones principales
self.totales = {
"Activo":0,
"Patrimonio neto":0,
"Pasivo":0
}
if libro is LibroDiario():
libro = LibroMayor(libro)
self.libro = libro # que debe ser de clase LibroMayor
self.poner_a_cero()
self.componer()
self.calcular_totales()
def componer(self) -> None:
for cuenta in self.libro.totales:
for seccion in BALANCE:
for subseccion in BALANCE[seccion]:
for subsubseccion in BALANCE[seccion][subseccion]:
if cuenta in BALANCE[seccion][subseccion][subsubseccion]:
if seccion == "Activo":
total_de_la_cuenta_a_añadir_al_balance=self.libro.totales[cuenta]
elif (seccion == "Patrimonio neto") or (seccion == "Pasivo"):
total_de_la_cuenta_a_añadir_al_balance=-self.libro.totales[cuenta]
else:
total_de_la_cuenta_a_añadir_al_balance=0
self[seccion][subseccion][subsubseccion]+=total_de_la_cuenta_a_añadir_al_balance
def poner_a_cero(self) -> None:
for seccion in self:
for subseccion in self[seccion]:
for subsubseccion in self[seccion][subseccion]:
self[seccion][subseccion][subsubseccion]=0
def calcular_totales(self) -> None:
self.totales["Activo"] = 0
self.totales["Patrimonio neto"] = 0
self.totales["Pasivo"] = 0
for seccion in BALANCE:
for subseccion in BALANCE[seccion]:
for subsubseccion in BALANCE[seccion][subseccion]:
if seccion == "Activo":
self.totales["Activo"] += self[seccion][subseccion][subsubseccion]
elif seccion == "Patrimonio neto":
self.totales["Patrimonio neto"] += self[seccion][subseccion][subsubseccion]
elif seccion == "Pasivo":
self.totales["Pasivo"] += self[seccion][subseccion][subsubseccion]
def __repr__(self):
representacion = ""
for seccion in self:
representacion+=f"\n{seccion}"
for subseccion in self[seccion]:
representacion+=f"\n\t{subseccion}"
for subsubseccion in self[seccion][subseccion]:
valor = self[seccion][subseccion][subsubseccion]
if valor != 0: representacion+=f"\n\t\t{subsubseccion}\t{valor}"
return representacion
class Contabilidad():
def __init__ (self):
self.libro_diario = LibroDiario()
self.actualizar_contabilidad()
def actualizar_contabilidad(self):
self.libro_mayor = LibroMayor(self.libro_diario)
self.regularizar_pyg()
self.balance = Balance(self.libro_mayor)
def añadir_asiento(self, concepto:str, debe:dict, haber:dict, fecha=dt.date.today()):
self.libro_diario.append(Asiento(concepto, debe, haber, fecha))
self.actualizar_contabilidad()
def regularizar_pyg(self):
self.pyg = PerdidasYGanancias(self.libro_diario, self.libro_mayor)