snappler_contable 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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