snappler_contable 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. checksums.yaml +15 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.rdoc +315 -0
  4. data/Rakefile +38 -0
  5. data/app/models/ledger_account.rb +151 -0
  6. data/app/models/ledger_account_activo.rb +6 -0
  7. data/app/models/ledger_account_pasivo.rb +6 -0
  8. data/app/models/ledger_account_patrimonio_neto.rb +6 -0
  9. data/app/models/ledger_account_resultado_negativo.rb +6 -0
  10. data/app/models/ledger_account_resultado_positivo.rb +6 -0
  11. data/app/models/ledger_currency.rb +3 -0
  12. data/app/models/ledger_entry.rb +3 -0
  13. data/app/models/ledger_move.rb +54 -0
  14. data/lib/generators/snappler_contable/initializer_generator.rb +9 -0
  15. data/lib/generators/snappler_contable/migrate_generator.rb +24 -0
  16. data/lib/generators/snappler_contable/templates/snappler_contable_app_ledger_accounts.rb +17 -0
  17. data/lib/generators/snappler_contable/templates/snappler_contable_migrate.rb +57 -0
  18. data/lib/snappler_contable/ext/string.rb +5 -0
  19. data/lib/snappler_contable/railtie.rb +4 -0
  20. data/lib/snappler_contable/snappler_contable.rb +131 -0
  21. data/lib/snappler_contable/snappler_contable_active_record_extension.rb +172 -0
  22. data/lib/snappler_contable/tree_node.rb +47 -0
  23. data/lib/snappler_contable/version.rb +3 -0
  24. data/lib/snappler_contable.rb +19 -0
  25. data/lib/tasks/snappler_contable_tasks.rake +4 -0
  26. data/test/dummy/README.rdoc +261 -0
  27. data/test/dummy/Rakefile +7 -0
  28. data/test/dummy/app/assets/javascripts/application.js +15 -0
  29. data/test/dummy/app/assets/stylesheets/application.css +13 -0
  30. data/test/dummy/app/controllers/application_controller.rb +3 -0
  31. data/test/dummy/app/helpers/application_helper.rb +2 -0
  32. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  33. data/test/dummy/config/application.rb +59 -0
  34. data/test/dummy/config/boot.rb +10 -0
  35. data/test/dummy/config/database.yml +25 -0
  36. data/test/dummy/config/environment.rb +5 -0
  37. data/test/dummy/config/environments/development.rb +37 -0
  38. data/test/dummy/config/environments/production.rb +67 -0
  39. data/test/dummy/config/environments/test.rb +37 -0
  40. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  41. data/test/dummy/config/initializers/inflections.rb +15 -0
  42. data/test/dummy/config/initializers/mime_types.rb +5 -0
  43. data/test/dummy/config/initializers/secret_token.rb +7 -0
  44. data/test/dummy/config/initializers/session_store.rb +8 -0
  45. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  46. data/test/dummy/config/locales/en.yml +5 -0
  47. data/test/dummy/config/routes.rb +58 -0
  48. data/test/dummy/config.ru +4 -0
  49. data/test/dummy/public/404.html +26 -0
  50. data/test/dummy/public/422.html +26 -0
  51. data/test/dummy/public/500.html +25 -0
  52. data/test/dummy/public/favicon.ico +0 -0
  53. data/test/dummy/script/rails +6 -0
  54. data/test/snappler_contable_test.rb +7 -0
  55. data/test/test_helper.rb +15 -0
  56. 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,6 @@
1
+ class LedgerAccountActivo < LedgerAccount
2
+
3
+ def process_balance(debe, haber)
4
+ debe - haber
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ class LedgerAccountPasivo < LedgerAccount
2
+
3
+ def process_balance(debe, haber)
4
+ haber - debe
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ class LedgerAccountPatrimonioNeto < LedgerAccount
2
+
3
+ def process_balance(debe, haber)
4
+ haber - debe
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ class LedgerAccountResultadoNegativo < LedgerAccount
2
+
3
+ def process_balance(debe, haber)
4
+ debe - haber
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ class LedgerAccountResultadoPositivo < LedgerAccount
2
+
3
+ def process_balance(debe, haber)
4
+ haber - debe
5
+ end
6
+ end
@@ -0,0 +1,3 @@
1
+ class LedgerCurrency < ActiveRecord::Base
2
+ attr_accessible :code, :name
3
+ end
@@ -0,0 +1,3 @@
1
+ class LedgerEntry < ActiveRecord::Base
2
+ has_many :ledger_moves, :dependent => :destroy
3
+ end
@@ -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