snappler_contable 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +315 -0
- data/Rakefile +38 -0
- data/app/models/ledger_account.rb +151 -0
- data/app/models/ledger_account_activo.rb +6 -0
- data/app/models/ledger_account_pasivo.rb +6 -0
- data/app/models/ledger_account_patrimonio_neto.rb +6 -0
- data/app/models/ledger_account_resultado_negativo.rb +6 -0
- data/app/models/ledger_account_resultado_positivo.rb +6 -0
- data/app/models/ledger_currency.rb +3 -0
- data/app/models/ledger_entry.rb +3 -0
- data/app/models/ledger_move.rb +54 -0
- data/lib/generators/snappler_contable/initializer_generator.rb +9 -0
- data/lib/generators/snappler_contable/migrate_generator.rb +24 -0
- data/lib/generators/snappler_contable/templates/snappler_contable_app_ledger_accounts.rb +17 -0
- data/lib/generators/snappler_contable/templates/snappler_contable_migrate.rb +57 -0
- data/lib/snappler_contable/ext/string.rb +5 -0
- data/lib/snappler_contable/railtie.rb +4 -0
- data/lib/snappler_contable/snappler_contable.rb +131 -0
- data/lib/snappler_contable/snappler_contable_active_record_extension.rb +172 -0
- data/lib/snappler_contable/tree_node.rb +47 -0
- data/lib/snappler_contable/version.rb +3 -0
- data/lib/snappler_contable.rb +19 -0
- data/lib/tasks/snappler_contable_tasks.rake +4 -0
- data/test/dummy/README.rdoc +261 -0
- data/test/dummy/Rakefile +7 -0
- data/test/dummy/app/assets/javascripts/application.js +15 -0
- data/test/dummy/app/assets/stylesheets/application.css +13 -0
- data/test/dummy/app/controllers/application_controller.rb +3 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/views/layouts/application.html.erb +14 -0
- data/test/dummy/config/application.rb +59 -0
- data/test/dummy/config/boot.rb +10 -0
- data/test/dummy/config/database.yml +25 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +37 -0
- data/test/dummy/config/environments/production.rb +67 -0
- data/test/dummy/config/environments/test.rb +37 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/inflections.rb +15 -0
- data/test/dummy/config/initializers/mime_types.rb +5 -0
- data/test/dummy/config/initializers/secret_token.rb +7 -0
- data/test/dummy/config/initializers/session_store.rb +8 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy/config/locales/en.yml +5 -0
- data/test/dummy/config/routes.rb +58 -0
- data/test/dummy/config.ru +4 -0
- data/test/dummy/public/404.html +26 -0
- data/test/dummy/public/422.html +26 -0
- data/test/dummy/public/500.html +25 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/dummy/script/rails +6 -0
- data/test/snappler_contable_test.rb +7 -0
- data/test/test_helper.rb +15 -0
- metadata +127 -0
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
YTk2MmZmNzQ2YThhMTgwMThjZDhmZjgxYWY3MzU1NzkwMWQ1YmI3ZQ==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
M2FmN2YyNzY1MDk5NWI2YjdmYjUxMDRmMWQ3OWQ5YWMxYTBkZDdhYw==
|
7
|
+
SHA512:
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
NGU2OTJiZmU2MzQ5NmVhN2NmMmJiMDJlYmU3MTVmZWI0ZjJhNjhmMDQ0MDQy
|
10
|
+
MjI3ODg0ZmIwMWFiY2FjNjBjNjBmYjgyMjMyYWQwOGIzZjk1Y2Y5NTJjOGQ1
|
11
|
+
NjBiMGU1ZWUyNzQ4NTI2YWViMTg1MGI2NjEzMTE5NGEwNmY2NzM=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
YTcyMTI1OWQ0MTI4NDdjY2QzNmQ0NjA1NzBiMTU1ODZjOGExNTVjYjE1MzY4
|
14
|
+
MjA0MmYxMDJlYzY3NTE1NzE4MzcwM2I3OTU0OTYwODI2NWRkYmE0OTNkNzlk
|
15
|
+
ZDY1MmM2Y2Y3NGY1NzVjNTdjZDQ5ZTE0NGM1ZGU0N2QxNGMzNDA=
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2013 YOURNAME
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,315 @@
|
|
1
|
+
= SnapplerContable
|
2
|
+
|
3
|
+
Esta gema agrega a una aplicacion la capacidad de manejar cuentas contables con el modelo teorico economico.
|
4
|
+
|
5
|
+
== Instalacion
|
6
|
+
|
7
|
+
Agregar esta linea al Gemfile
|
8
|
+
|
9
|
+
gem "snappler_contable"
|
10
|
+
|
11
|
+
Ejecutar
|
12
|
+
|
13
|
+
$ bundle install
|
14
|
+
|
15
|
+
Después de instalar se deben correr estos comandos
|
16
|
+
|
17
|
+
$ rails g snappler_contable:initializer
|
18
|
+
$ rails g snappler_contable:migrate
|
19
|
+
$ rake db:migrate
|
20
|
+
|
21
|
+
Esto va a crear el achivo:
|
22
|
+
|
23
|
+
config/initializers/snappler_contable.rb
|
24
|
+
|
25
|
+
Donde se tienen que agregar todas las operaciones contables se van a ejecutar en el sistema.
|
26
|
+
|
27
|
+
Tambien va a crear dos migraciones:
|
28
|
+
|
29
|
+
aaaammdd_snappler_contable_migrate.rb
|
30
|
+
|
31
|
+
Que crea la tabla de cuentas, de movimientos, de asientos y de monedas.
|
32
|
+
Se crean 5 registros en la tabla LedgerAccounts que son necesarios para el sistema:
|
33
|
+
|
34
|
+
LedgerAccountActivo : Activo
|
35
|
+
LedgerAccountPasivo : Pasivo
|
36
|
+
LedgerAccountPatrimonioNeto : Patrimonio Neto
|
37
|
+
LedgerAccountResultadoPositivo : Resultado Positivo
|
38
|
+
LedgerAccountResultadoNegativo : Resultado Negativo
|
39
|
+
|
40
|
+
Tambien crea la moneda por defecto:
|
41
|
+
LedgerCurrency.create(name: 'Peso', code: 'ARS')
|
42
|
+
|
43
|
+
Luego crea otra migración llamada:
|
44
|
+
|
45
|
+
aaaammdd_snappler_contable_app_ledger_accounts.rb
|
46
|
+
|
47
|
+
En la que el programador debe agregar las cuentas necesarias para el plan de cuentas de la aplicacion.
|
48
|
+
Ya están cargadas en variables de instancia las 5 cuentas originales.
|
49
|
+
Luego se usa el metodo 'add_child' para agregar cuentas hijas.
|
50
|
+
|
51
|
+
activo = LedgerAccountActivo.first #en ese momento solo existe una
|
52
|
+
sub_cuenta_activo = activo.add_child('Sub cuenta activo')
|
53
|
+
|
54
|
+
Esta función se puede usar en cualquier parte de la applicación.
|
55
|
+
La cuenta hija será de la misma clase que la clase padre.
|
56
|
+
|
57
|
+
== Uso
|
58
|
+
|
59
|
+
La gema va a trabajar con:
|
60
|
+
|
61
|
+
1) Una estructura de tipo árbol de objetos. Cada objeto es una instancia de una de las 5 sub clases de LedgerAccount.
|
62
|
+
El árbol tiene una raiz abstracta y 5 nodos:
|
63
|
+
|
64
|
+
LedgerAccountActivo : Activo
|
65
|
+
LedgerAccountPasivo : Pasivo
|
66
|
+
LedgerAccountPatrimonioNeto : Patrimonio Neto
|
67
|
+
LedgerAccountResultadoPositivo : Resultado Positivo
|
68
|
+
LedgerAccountResultadoNegativo : Resultado Negativo
|
69
|
+
|
70
|
+
Luego se agregaran las cuentas que completen el plan de cuentas específico para la aplicación.
|
71
|
+
Una vez creadas las cuentas del plan, quedaría (por ejemplo) así:
|
72
|
+
|
73
|
+
=> ROOT
|
74
|
+
1 : Activo (1)
|
75
|
+
1.1 : Disponibilidades (2)
|
76
|
+
1.1.1 : Caja (3)
|
77
|
+
1.1.2 : Bancos (4)
|
78
|
+
1.1.2.1 : Cuenta Corriente (5)
|
79
|
+
1.1.2.2 : Caja de Ahorro (6)
|
80
|
+
1.2 : Cuentas x cobrar (7)
|
81
|
+
1.3 : Bienes de cambio (8)
|
82
|
+
1.4 : Bienes de uso (9)
|
83
|
+
1.4.1 : Rodados (10)
|
84
|
+
2 : Pasivo (11)
|
85
|
+
2.1 : Proveedores (12)
|
86
|
+
2.2 : Banco pasivo (13)
|
87
|
+
2.2.1 : Prestamo (14)
|
88
|
+
3 : Patrimonio Neto (15)
|
89
|
+
3.1 : Capital Social (16)
|
90
|
+
4 : Resultado Positivo (17)
|
91
|
+
4.1 : Ventas (18)
|
92
|
+
4.2 : Diferencia de Stock (19)
|
93
|
+
4.3 : Arqueo (20)
|
94
|
+
4.3.1 : Arqueo banco (21)
|
95
|
+
4.3.2 : Arqueo caja (22)
|
96
|
+
5 : Resultado Negativo (23)
|
97
|
+
5.1 : Costo mercaderia vendida (24)
|
98
|
+
5.2 : Diferencia de Stock negativo (25)
|
99
|
+
5.3 : Arqueo negativo (26)
|
100
|
+
5.3.1 : Arqueo negativo banco (27)
|
101
|
+
5.3.2 : Arqueo negativo caja (28)
|
102
|
+
|
103
|
+
(NOTA: el número entre paréntesis es el id del objeto)
|
104
|
+
|
105
|
+
2) Un archivo de tipo 'initializer':
|
106
|
+
/config/initializers/snappler_contable.rb
|
107
|
+
|
108
|
+
# Agregar el array de operaciones validas
|
109
|
+
SnapplerContable.valid_operations = [:cobro, :pago, :deposito]
|
110
|
+
|
111
|
+
Operaciones válidas para ejecutar con la gema. Las operaciones válidas son aquellas necesarias para vincular un modelo normal de la aplicacion con una cuenta subclase de LedgerAccount.
|
112
|
+
|
113
|
+
# Moneda por defecto
|
114
|
+
SnapplerContable.default_currency = 'ARS'
|
115
|
+
Moneda usada por la gema por defecto. En particular setea 'ARS'. Para modificarla hay que crear la moneda que se desea y poner el códigod de la nueva moneda. Si se cambia se tiene que reiniciar la aplicación.
|
116
|
+
|
117
|
+
|
118
|
+
3) Modelos comunes de la aplicación, los cuales se pueden vincular con una o mas cuentas del plan de cuentas existente.
|
119
|
+
|
120
|
+
Un modelo (hijo de ActiveRecord) puede vincularse al plan de cuentas mediante la sentencia:
|
121
|
+
|
122
|
+
class Product < ActiveRecord::Base
|
123
|
+
...
|
124
|
+
|
125
|
+
act_as_snappler_contable( :accounts => [:bienes_de_cambio, :cuentas_x_cobrar],
|
126
|
+
:account_by_operation => {:pago => :bienes_de_cambio,
|
127
|
+
:cobro => :cuentas_x_cobrar})
|
128
|
+
...
|
129
|
+
end
|
130
|
+
|
131
|
+
En este caso la clase Product está vinculada con la cuenta :bienes_de_cambio y :cuentas_x_cobrar
|
132
|
+
Esto significa que las instancias de la clase Product crearán cuentas de la misma clase que :bienes_de_cambio y :cuentas_x_cobrar, y serán hijas de la cuenta respectiva.
|
133
|
+
|
134
|
+
El array :accounts contiene todas las cuentas con las que estará vinculada la clase Product.
|
135
|
+
El array :account_by_operation indica cual la cuenta padre con la que se vinculará, en función la operación que se pase por parámetro al momento de ejecutar un movimiento contable.
|
136
|
+
|
137
|
+
Esto significa que cuando se ejecute un movimiento contable con operación :pago que involucre a un objeto de tipo Product, por ejemplo "Harina", se agregará una nueva cuenta, hija de :bienes_de_pago, vinculada con el producto "Harina". El plan de cuentas quedará así:
|
138
|
+
|
139
|
+
=> ROOT
|
140
|
+
1 : Activo (1)
|
141
|
+
1.1 : Disponibilidades (2)
|
142
|
+
1.1.1 : Caja (3)
|
143
|
+
1.1.2 : Bancos (4)
|
144
|
+
1.1.2.1 : Cuenta Corriente (5)
|
145
|
+
1.1.2.2 : Caja de Ahorro (6)
|
146
|
+
1.2 : Cuentas x cobrar (7)
|
147
|
+
1.3 : Bienes de cambio (8)
|
148
|
+
1.3.1 : Bienes De Cambio Product:Harina (40)
|
149
|
+
...
|
150
|
+
|
151
|
+
Existe la posibilidad que haya modelos relacionados entre sí que tengan que vincularse con el plan de cuentas.
|
152
|
+
Un ejemplo podría ser la cuenta Bancos se va a relacionar con Objetos de tipo banco de la aplicación, y a su vez estos bancos tendran cajas de ahorro que, logicamente, tienen que estar integradas al plan de cuentas.
|
153
|
+
|
154
|
+
|
155
|
+
Plan de cuentas
|
156
|
+
=> ROOT
|
157
|
+
1 : Activo (1)
|
158
|
+
1.1 : Disponibilidades (2)
|
159
|
+
1.1.1 : Caja (3)
|
160
|
+
1.1.2 : Bancos (4)
|
161
|
+
|
162
|
+
Modelos del sistema:
|
163
|
+
Bank
|
164
|
+
BankAccount
|
165
|
+
|
166
|
+
|
167
|
+
La única relación permitida de 'belongs_to'.
|
168
|
+
|
169
|
+
En este caso los modelos quedarían así:
|
170
|
+
|
171
|
+
class Bank < ActiveRecord::Base
|
172
|
+
act_as_snappler_contable(:accounts => [:bancos],
|
173
|
+
:account_by_operation => {})
|
174
|
+
...
|
175
|
+
end
|
176
|
+
|
177
|
+
class BankAccount < Account
|
178
|
+
belongs_to :bank
|
179
|
+
act_as_snappler_contable(:accounts => [{:bancos => :bank}],
|
180
|
+
:account_by_operation => {:deposito => :bancos, :extraccion => :bancos, :transferencia => :bancos})
|
181
|
+
end
|
182
|
+
|
183
|
+
Como se ve, en la clase BankAccount, en el parametro :accounts se indica un hash:
|
184
|
+
|
185
|
+
{:bancos => :bank}
|
186
|
+
|
187
|
+
Esto significa que BanckAccount está en la rama de la cuenta contable :bancos a través del la relación "belongs_to :bank"
|
188
|
+
|
189
|
+
Entonces tendríamos una clase "inmediata" a las cuentas contables (en este caso Bank), que en su parametro :accounts indica su cuenta contable padre (:bancos), y luego clases que cuelgan de la "inmediata" (BankAccount) que en el parametro :accounts tienen que indicar cual es la cuenta contable superior, y a través de qué relación la alcanza (:bank, ya que tiene declarado un "belongs_to :bank").
|
190
|
+
|
191
|
+
Las clases hacia "abajo" no tienen límite, siempre y cuando indiquen cual es la cuenta contable superior.
|
192
|
+
El ejemplo ya no tiene significado real, pero supongamos que la cuenta puede tener una sub-cuenta, en este caso la clase se vería así:
|
193
|
+
|
194
|
+
class SubBankAccount < Account
|
195
|
+
belongs_to :bank_account
|
196
|
+
act_as_snappler_contable(:accounts => [{:bancos => :bank_account}],
|
197
|
+
:account_by_operation => {:deposito => :bancos, :extraccion => :bancos, :transferencia => :bancos})
|
198
|
+
end
|
199
|
+
|
200
|
+
De esta forma, una vez ejecutada una operación contable que involucre estos objetos, el plan de cuentas se vería así:
|
201
|
+
|
202
|
+
=> ROOT
|
203
|
+
1 : Activo (1)
|
204
|
+
1.1 : Disponibilidades (2)
|
205
|
+
1.1.1 : Caja (3)
|
206
|
+
1.1.2 : Bancos (4)
|
207
|
+
1.1.2.1 : Cuenta Corriente (5)
|
208
|
+
1.1.2.2 : Caja de Ahorro (6)
|
209
|
+
1.1.2.3 : Bancos Bank:Patagonia (41)
|
210
|
+
1.1.2.3.1 : Bancos Bank:Patagonia BankAccount:Cc Banco Patagonia (42)
|
211
|
+
1.1.2.3.1.1 : Bancos Bank:Patagonia BankAccount:Cc Banco Patagonia SubBankAccount:Sub Cc Banco Patagonia (45)
|
212
|
+
|
213
|
+
|
214
|
+
== Funciones del módulo SnapplerContable
|
215
|
+
|
216
|
+
Para operar con la gema, algunas de las operaciones se ejecutan directamente como métodos de clase de SnapplerContable.
|
217
|
+
|
218
|
+
- SnapplerContable.accounts_tree
|
219
|
+
Devuelve el plan de cuentas como un árbol.
|
220
|
+
Se imprime indentando y con el código de plan de cuentas.
|
221
|
+
El arbol implementa "each", de esa forma se puede tratar con las operaciones de coleccion conocidas.
|
222
|
+
|
223
|
+
SnapplerContable.accounts_tree.each {|a| puts a.code}
|
224
|
+
|
225
|
+
|
226
|
+
- SnapplerContable.re_code_tree
|
227
|
+
Si se llega a cambiar el código de alguna de la cuentas de la raiz, o se cambia el valor el campo 'order' se debe ejecutar el método
|
228
|
+
|
229
|
+
SnapplerContable.re_code_tree
|
230
|
+
|
231
|
+
De esta forma todos los códigos se reacomodarán.
|
232
|
+
|
233
|
+
== Funciones del modelo de cuentas contables (subclases de LedgerAccount)
|
234
|
+
|
235
|
+
- cuenta.balance
|
236
|
+
Devuelve el saldo para la cuenta.
|
237
|
+
Usa el campo 'balance_sum' que se actualiza en cada LedgerAccount a medida que se agregan movimientos LedgerMoves.
|
238
|
+
Cada cuenta tiene su saldo actualizado.
|
239
|
+
Para devolverlo se consulta por los saldos de todas las cuentas hijas.
|
240
|
+
|
241
|
+
- cuenta.balance_to(to_date)
|
242
|
+
Da el saldo calculando con los movimientos hasta la fecha 'to_date'
|
243
|
+
|
244
|
+
- cuenta.balance_from(from_date)
|
245
|
+
Da el saldo calculando con los movimientos desde la fecha 'from_date'
|
246
|
+
|
247
|
+
- cuenta.balance_from(from_date)
|
248
|
+
Da el saldo calculando con los movimientos desde la fecha 'from_date' hasta la fecha 'to_date'
|
249
|
+
|
250
|
+
Las funciones de saldo con fecha usan la tabla LedgerMoves de esta forma:
|
251
|
+
Hace el cálculo del saldo de la cuenta, teniendo en cuenta los movimientos vinculados con dicha cuenta y con todas las cuentas de su sub árbol.
|
252
|
+
El cálculo del saldo va a depender de la clase de la cuenta:
|
253
|
+
|
254
|
+
LedgerAccountActivo : debe - haber
|
255
|
+
LedgerAccountPasivo : haber - debe
|
256
|
+
LedgerAccountPatrimonioNeto : haber - debe
|
257
|
+
LedgerAccountResultadoPositivo : haber - debe
|
258
|
+
LedgerAccountResultadoNegativo : debe - haber
|
259
|
+
|
260
|
+
las operacioes cuenta.balance y cuenta.balance_to(fecha del ultimo movimiento registrado) tienen que dar lo mismo.
|
261
|
+
|
262
|
+
|
263
|
+
|
264
|
+
- cuenta.accounts_tree
|
265
|
+
Devuelve el sub árbol de cuentas que baja desde la cuenta sobre la que se ejecuta el comando
|
266
|
+
|
267
|
+
== Funciones agregadas por act_as_snappler_contable
|
268
|
+
|
269
|
+
- objeto_act_as_snp_contable.ledger_accounts
|
270
|
+
Devuelve un array con las cuentas contables (las crea si es necesario) vinculadas con el objeto.
|
271
|
+
|
272
|
+
|
273
|
+
= Operación contable
|
274
|
+
|
275
|
+
- SnapplerContable.op
|
276
|
+
Es la operación que registra los movimientos para la cuentas.
|
277
|
+
|
278
|
+
El formato es:
|
279
|
+
|
280
|
+
SnapplerContable.op(Colección movimientos debe, Colección movimientos haber, operación debe, operación haber )
|
281
|
+
|
282
|
+
Las colecciones de movimientos debe/haber tiene un formato análogo.
|
283
|
+
|
284
|
+
Colección movimientos debe/haber:
|
285
|
+
[{account: LedgerAccount u objeto de clase act_as_snappler_contable,
|
286
|
+
value: valor,
|
287
|
+
order: orden del movimiento (opcional) (solo funciona si TODOS los movimientos tienen order),
|
288
|
+
currency: id (integer) u objeto LedgerCurrency (opcional, ARS por defecto),
|
289
|
+
currency_ratio: cotización de la moneda (opcional, 1 por defecto),
|
290
|
+
operation: operacion para extraer cuenta contable, en caso de que account: sea objeto de clase act_as_snappler_contable (opcional) }]
|
291
|
+
|
292
|
+
La suma de los values de "Colección movimientos debe" y "Colección movimientos haber" deben ser iguales.
|
293
|
+
|
294
|
+
En caso de que en el parámetro 'account:' de alguno de los movimientos sea un objeto act_as_snappler_contable, se debe especificar la operación que extrae la cuenta contable correspondiente.
|
295
|
+
Hay que tomarla del parametro ':account_by_operation' del act_as_snappler_contable.
|
296
|
+
|
297
|
+
Si el objeto está la "Colección movimientos debe" o "Colección movimientos haber", la operación se pasa como tercer parámetro.
|
298
|
+
En caso de que haya objetos en "Coleccion movimientos debe" y "Colección movimientos haber" que deban extraerse con operaciones distintas, se pasan dos parámetros más, siendo el primero usado para la colección del debe y el segudo para el haber.
|
299
|
+
|
300
|
+
Por último, respecto a las operaciones, si dentro de alguno las colecciones de movivientos hay objetos de los que se deben extraer cuentas con operaciones distintas, el hash de movimiento soporta el parámetro :operation.
|
301
|
+
|
302
|
+
|
303
|
+
|
304
|
+
|
305
|
+
|
306
|
+
|
307
|
+
|
308
|
+
|
309
|
+
|
310
|
+
|
311
|
+
|
312
|
+
|
313
|
+
|
314
|
+
|
315
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
begin
|
3
|
+
require 'bundler/setup'
|
4
|
+
rescue LoadError
|
5
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
6
|
+
end
|
7
|
+
begin
|
8
|
+
require 'rdoc/task'
|
9
|
+
rescue LoadError
|
10
|
+
require 'rdoc/rdoc'
|
11
|
+
require 'rake/rdoctask'
|
12
|
+
RDoc::Task = Rake::RDocTask
|
13
|
+
end
|
14
|
+
|
15
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
16
|
+
rdoc.rdoc_dir = 'rdoc'
|
17
|
+
rdoc.title = 'SnapplerContable'
|
18
|
+
rdoc.options << '--line-numbers'
|
19
|
+
rdoc.rdoc_files.include('README.rdoc')
|
20
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
|
25
|
+
|
26
|
+
Bundler::GemHelper.install_tasks
|
27
|
+
|
28
|
+
require 'rake/testtask'
|
29
|
+
|
30
|
+
Rake::TestTask.new(:test) do |t|
|
31
|
+
t.libs << 'lib'
|
32
|
+
t.libs << 'test'
|
33
|
+
t.pattern = 'test/**/*_test.rb'
|
34
|
+
t.verbose = false
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
task :default => :test
|
@@ -0,0 +1,151 @@
|
|
1
|
+
class LedgerAccount < ActiveRecord::Base
|
2
|
+
#--------------------------------------------- RELATIION
|
3
|
+
belongs_to :contable, polymorphic: true
|
4
|
+
has_many :child_ledger_accounts, :class_name => "LedgerAccount", :foreign_key => "master_ledger_account_id", :order => 'order_column'
|
5
|
+
belongs_to :master_ledger_account, :class_name => "LedgerAccount"
|
6
|
+
#--------------------------------------------- MISC
|
7
|
+
attr_accessible :name, :code, :code_name, :master_ledger_account_id, :master_ledger_account, :contable, :balance_sum
|
8
|
+
#--------------------------------------------- VALIDATION
|
9
|
+
validates :master_ledger_account_id, :presence => true
|
10
|
+
validates :code_name, :uniqueness => true
|
11
|
+
#--------------------------------------------- CALLBACK
|
12
|
+
before_save :set_code
|
13
|
+
after_create :set_order_column
|
14
|
+
#--------------------------------------------- SCOPES
|
15
|
+
default_scope order('order_column ASC')
|
16
|
+
#--------------------------------------------- METHODS
|
17
|
+
|
18
|
+
def self.account(value)
|
19
|
+
where(:code_name => value.to_s.snp_underscore).first
|
20
|
+
end
|
21
|
+
|
22
|
+
def add_child(name)
|
23
|
+
if self.persisted?
|
24
|
+
self.class.create(name: name, master_ledger_account: self )
|
25
|
+
else
|
26
|
+
if self.errors.count > 0
|
27
|
+
errores = " La cuenta '#{name}' no se pudo persistir porque sus campos no cumplen la validacion de LedgerAccount."
|
28
|
+
end
|
29
|
+
raise "La instancia debe estar persistida para poder agregar una cuenta hija." + errores.to_s
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def set_code
|
34
|
+
unless self.master_ledger_account_id == 0
|
35
|
+
unless self.order_column.nil?
|
36
|
+
self.code = "#{self.master_ledger_account.code}.#{self.order_column}"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def re_code(parent_code)
|
42
|
+
self.code = "#{parent_code}#{parent_code.blank? ? '' : '.'}#{order_column}"
|
43
|
+
save
|
44
|
+
child_ledger_accounts.each do |child|
|
45
|
+
child.re_code(code)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def name=(value)
|
50
|
+
write_attribute :name, value
|
51
|
+
if (read_attribute(:code_name)).blank?
|
52
|
+
name_value = read_attribute(:name)
|
53
|
+
write_attribute :code_name, name_value.snp_underscore
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def set_order_column
|
58
|
+
#Solo agrega orden si la cuenta no es tope del arbol
|
59
|
+
unless self.master_ledger_account_id == 0
|
60
|
+
max = self.master_ledger_account.child_ledger_accounts.maximum(:order_column)
|
61
|
+
else
|
62
|
+
max = LedgerAccount.where(:master_ledger_account_id => 0).maximum(:order_column)
|
63
|
+
end
|
64
|
+
|
65
|
+
if max.nil?
|
66
|
+
self.order_column = 1
|
67
|
+
else
|
68
|
+
self.order_column = max + 1
|
69
|
+
end
|
70
|
+
|
71
|
+
self.save
|
72
|
+
end
|
73
|
+
|
74
|
+
def children_accounts_ids
|
75
|
+
SnapplerContable.account_sub_tree(self).collect{|account| account.id}
|
76
|
+
end
|
77
|
+
|
78
|
+
def saldo
|
79
|
+
#alias para compatibilidad
|
80
|
+
balance
|
81
|
+
end
|
82
|
+
|
83
|
+
def balance
|
84
|
+
bal = LedgerAccount.where(:id => children_accounts_ids).sum(:balance_sum)
|
85
|
+
LedgerMove.format_value(bal)
|
86
|
+
end
|
87
|
+
|
88
|
+
|
89
|
+
def balance_to(to_date)
|
90
|
+
dh_hash = LedgerMove.where(:ledger_account_id => children_accounts_ids).where('created_at <= ?', to_date.end_of_day).group(:dh).sum(:value)
|
91
|
+
debe = dh_hash["D"].to_i
|
92
|
+
haber = dh_hash["H"].to_i
|
93
|
+
LedgerMove.format_value(process_balance(debe, haber))
|
94
|
+
end
|
95
|
+
|
96
|
+
def balance_from(from_date)
|
97
|
+
dh_hash = LedgerMove.where(:ledger_account_id => children_accounts_ids).where('created_at >= ?', from_date.beginning_of_day).group(:dh).sum(:value)
|
98
|
+
debe = dh_hash["D"].to_i
|
99
|
+
haber = dh_hash["H"].to_i
|
100
|
+
LedgerMove.format_value(process_balance(debe, haber))
|
101
|
+
end
|
102
|
+
|
103
|
+
def balance_from_to(from_date, to_date)
|
104
|
+
dh_hash = LedgerMove.where(:ledger_account_id => children_accounts_ids).where('created_at >= ? AND created_at <= ?', from_date.beginning_of_day, to_date.end_of_day).group(:dh).sum(:value)
|
105
|
+
debe = dh_hash["D"].to_i
|
106
|
+
haber = dh_hash["H"].to_i
|
107
|
+
LedgerMove.format_value(process_balance(debe, haber))
|
108
|
+
end
|
109
|
+
|
110
|
+
def process_balance
|
111
|
+
raise "Este metodo solo se tiene que implementar en las subclases"
|
112
|
+
end
|
113
|
+
|
114
|
+
def update_balance(value, dh)
|
115
|
+
case dh.upcase
|
116
|
+
when 'D'
|
117
|
+
update_balance = process_balance(value, 0)
|
118
|
+
when 'H'
|
119
|
+
update_balance = process_balance(0, value)
|
120
|
+
end
|
121
|
+
bal = self.balance_sum
|
122
|
+
self.balance_sum = bal + update_balance
|
123
|
+
save
|
124
|
+
end
|
125
|
+
|
126
|
+
def update_balance_destroy(value, dh)
|
127
|
+
#inverse operation
|
128
|
+
ops = {'D' => 'H', 'H' => 'D'}
|
129
|
+
update_balance(value, ops[dh])
|
130
|
+
end
|
131
|
+
|
132
|
+
def accounts_tree
|
133
|
+
SnapplerContable.account_sub_tree(self)
|
134
|
+
end
|
135
|
+
|
136
|
+
def balance_sum=(val)
|
137
|
+
if val.is_a? Numeric
|
138
|
+
write_attribute :balance_sum, LedgerMove.unformat_value(val)
|
139
|
+
else
|
140
|
+
raise "El valor debe ser un numero"
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def balance_sum
|
145
|
+
value_formated = read_attribute :balance_sum
|
146
|
+
LedgerMove.format_value(value_formated)
|
147
|
+
end
|
148
|
+
|
149
|
+
end
|
150
|
+
|
151
|
+
|
@@ -0,0 +1,54 @@
|
|
1
|
+
class LedgerMove < ActiveRecord::Base
|
2
|
+
#--------------------------------------------- RELATION
|
3
|
+
belongs_to :ledger_entry
|
4
|
+
belongs_to :ledger_account
|
5
|
+
belongs_to :ledger_currency
|
6
|
+
#--------------------------------------------- MISC
|
7
|
+
attr_accessible :currency_ratio, :dh, :value, :ledger_account, :ledger_currency, :currency_ratio
|
8
|
+
DIVISOR = 100.0
|
9
|
+
#--------------------------------------------- VALIDATION
|
10
|
+
|
11
|
+
#--------------------------------------------- CALLBACK
|
12
|
+
after_create :update_balance_create
|
13
|
+
after_destroy :update_balance_destroy
|
14
|
+
after_update :update_balance_update
|
15
|
+
|
16
|
+
#--------------------------------------------- SCOPES
|
17
|
+
|
18
|
+
#--------------------------------------------- METHODS
|
19
|
+
|
20
|
+
def update_balance_create
|
21
|
+
self.ledger_account.update_balance(value, dh)
|
22
|
+
end
|
23
|
+
|
24
|
+
def update_balance_destroy
|
25
|
+
self.ledger_account.update_balance_destroy(value, dh)
|
26
|
+
end
|
27
|
+
|
28
|
+
def update_balance_update
|
29
|
+
self.ledger_account.update_balance_destroy( self.class.format_value(value_was), dh)
|
30
|
+
self.ledger_account.update_balance(value, dh)
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
def self.format_value(value)
|
35
|
+
value / DIVISOR
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.unformat_value(value)
|
39
|
+
(value * DIVISOR).to_i
|
40
|
+
end
|
41
|
+
|
42
|
+
def value=(val)
|
43
|
+
if val.is_a? Numeric
|
44
|
+
write_attribute :value, self.class.unformat_value(val)
|
45
|
+
else
|
46
|
+
raise "El valor debe ser un numero"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def value
|
51
|
+
value_formated = read_attribute :value
|
52
|
+
self.class.format_value(value_formated)
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
|
3
|
+
module SnapplerContable
|
4
|
+
class InitializerGenerator < ::Rails::Generators::Base
|
5
|
+
def create_initializer_file
|
6
|
+
create_file "config/initializers/snappler_contable.rb", "# Agregar el array de operaciones validas\nSnapplerContable.valid_operations = [:pago]\n\n# Moneda por defecto\nSnapplerContable.default_currency = 'ARS'"
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
require 'rails/generators/migration'
|
3
|
+
|
4
|
+
module SnapplerContable
|
5
|
+
class MigrateGenerator < ::Rails::Generators::Base
|
6
|
+
include Rails::Generators::Migration
|
7
|
+
source_root File.expand_path('../templates', __FILE__)
|
8
|
+
|
9
|
+
desc "add snappler_contable migration"
|
10
|
+
def self.next_migration_number(path)
|
11
|
+
unless @prev_migration_nr
|
12
|
+
@prev_migration_nr = Time.now.utc.strftime("%Y%m%d%H%M%S").to_i
|
13
|
+
else
|
14
|
+
@prev_migration_nr += 1
|
15
|
+
end
|
16
|
+
@prev_migration_nr.to_s
|
17
|
+
end
|
18
|
+
|
19
|
+
def copy_migrations
|
20
|
+
migration_template "snappler_contable_migrate.rb", "db/migrate/snappler_contable_migrate.rb"
|
21
|
+
migration_template "snappler_contable_app_ledger_accounts.rb", "db/migrate/snappler_contable_app_ledger_accounts.rb"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|