stockor-core 0.2

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 (221) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +18 -0
  3. data/Gemfile +6 -0
  4. data/Guardfile +13 -0
  5. data/LICENSE.txt +674 -0
  6. data/README.md +88 -0
  7. data/Rakefile +58 -0
  8. data/config/database.yml +9 -0
  9. data/db/migrate/20120110142845_create_skr_sequential_ids.rb +35 -0
  10. data/db/migrate/20140202185309_create_skr_gl_accounts.rb +15 -0
  11. data/db/migrate/20140202193316_create_skr_gl_periods.rb +16 -0
  12. data/db/migrate/20140202193318_create_skr_gl_transactions.rb +14 -0
  13. data/db/migrate/20140202193319_create_skr_gl_postings.rb +16 -0
  14. data/db/migrate/20140202193700_create_skr_gl_manual_entries.rb +13 -0
  15. data/db/migrate/20140213040608_create_skr_payment_terms.rb +16 -0
  16. data/db/migrate/20140220031700_create_skr_addresses.rb +19 -0
  17. data/db/migrate/20140220031800_create_skr_locations.rb +19 -0
  18. data/db/migrate/20140220190836_create_skr_vendors.rb +22 -0
  19. data/db/migrate/20140220203029_create_skr_customers.rb +22 -0
  20. data/db/migrate/20140224034759_create_skr_skus.rb +22 -0
  21. data/db/migrate/20140225032853_create_skr_sku_locs.rb +21 -0
  22. data/db/migrate/20140320030501_create_skr_uoms.rb +19 -0
  23. data/db/migrate/20140321031604_create_skr_sku_vendors.rb +18 -0
  24. data/db/migrate/20140322012143_create_skr_ia_reasons.rb +14 -0
  25. data/db/migrate/20140322014401_create_skr_inventory_adjustments.rb +16 -0
  26. data/db/migrate/20140322023453_create_skr_ia_lines.rb +18 -0
  27. data/db/migrate/20140322035024_create_skr_sku_trans.rb +21 -0
  28. data/db/migrate/20140322223912_create_skr_sales_orders.rb +27 -0
  29. data/db/migrate/20140322223920_create_skr_so_lines.rb +25 -0
  30. data/db/migrate/20140323001446_create_so_details_view.rb +81 -0
  31. data/db/migrate/20140327202102_create_skr_purchase_orders.rb +20 -0
  32. data/db/migrate/20140327202107_create_skr_po_lines.rb +25 -0
  33. data/db/migrate/20140327202207_create_skr_pick_tickets.rb +16 -0
  34. data/db/migrate/20140327202209_create_skr_pt_lines.rb +23 -0
  35. data/db/migrate/20140327224000_create_skr_invoices.rb +25 -0
  36. data/db/migrate/20140327224002_create_skr_inv_lines.rb +23 -0
  37. data/db/migrate/20140330232808_create_skr_sku_loc_details_view.rb +31 -0
  38. data/db/migrate/20140330232810_create_skr_sku_qty_details_view.rb +48 -0
  39. data/db/migrate/20140400164729_create_skr_vouchers.rb +22 -0
  40. data/db/migrate/20140400164733_create_skr_vo_lines.rb +21 -0
  41. data/db/migrate/20140401164729_create_skr_po_receipt.rb +16 -0
  42. data/db/migrate/20140401164740_create_skr_por_line.rb +21 -0
  43. data/db/migrate/20140422024010_create_skr_inv_details_view.rb +42 -0
  44. data/lib/generators/stockor/migrations/install_generator.rb +42 -0
  45. data/lib/skr/address.rb +97 -0
  46. data/lib/skr/business_entity.rb +25 -0
  47. data/lib/skr/concerns/acts_as_uom.rb +47 -0
  48. data/lib/skr/concerns/all.rb +30 -0
  49. data/lib/skr/concerns/association_extensions.rb +85 -0
  50. data/lib/skr/concerns/attr_accessor_with_default.rb +54 -0
  51. data/lib/skr/concerns/code_identifier.rb +43 -0
  52. data/lib/skr/concerns/export_associations.rb +52 -0
  53. data/lib/skr/concerns/export_join_tables.rb +39 -0
  54. data/lib/skr/concerns/export_methods.rb +104 -0
  55. data/lib/skr/concerns/export_scope.rb +66 -0
  56. data/lib/skr/concerns/exported_limit_evaluator.rb +17 -0
  57. data/lib/skr/concerns/gl_tran_extensions.rb +18 -0
  58. data/lib/skr/concerns/has_gl_transaction.rb +67 -0
  59. data/lib/skr/concerns/has_sku_loc_lines.rb +47 -0
  60. data/lib/skr/concerns/immutable_model.rb +32 -0
  61. data/lib/skr/concerns/inv_extensions.rb +24 -0
  62. data/lib/skr/concerns/is_order_like.rb +47 -0
  63. data/lib/skr/concerns/is_sku_loc_line.rb +65 -0
  64. data/lib/skr/concerns/json_attribute_access.rb +55 -0
  65. data/lib/skr/concerns/locked_fields.rb +84 -0
  66. data/lib/skr/concerns/pt_extensions.rb +22 -0
  67. data/lib/skr/concerns/pub_sub.rb +105 -0
  68. data/lib/skr/concerns/queries.rb +20 -0
  69. data/lib/skr/concerns/random_hash_code.rb +40 -0
  70. data/lib/skr/concerns/sanitize_json.rb +49 -0
  71. data/lib/skr/concerns/sku_extensions.rb +52 -0
  72. data/lib/skr/concerns/so_extensions.rb +30 -0
  73. data/lib/skr/concerns/state_machine.rb +62 -0
  74. data/lib/skr/concerns/track_modifications.rb +48 -0
  75. data/lib/skr/concerns/visible_id_identifier.rb +53 -0
  76. data/lib/skr/core.rb +30 -0
  77. data/lib/skr/core/configuration.rb +87 -0
  78. data/lib/skr/core/db.rb +82 -0
  79. data/lib/skr/core/db/migration_helpers.rb +178 -0
  80. data/lib/skr/core/db/migrations.rb +15 -0
  81. data/lib/skr/core/db/seed.rb +46 -0
  82. data/lib/skr/core/db/seed/chart_of_accounts.yml +168 -0
  83. data/lib/skr/core/db/seed/payment_terms.yml +60 -0
  84. data/lib/skr/core/logger.rb +36 -0
  85. data/lib/skr/core/numbers.rb +71 -0
  86. data/lib/skr/core/rails_engine.rb +5 -0
  87. data/lib/skr/core/standard_pricing_provider.rb +15 -0
  88. data/lib/skr/core/strings.rb +57 -0
  89. data/lib/skr/core/testing.rb +11 -0
  90. data/lib/skr/core/testing/assertions.rb +44 -0
  91. data/lib/skr/core/testing/fixtures.rb +25 -0
  92. data/lib/skr/core/testing/fixtures/skr/addresses.yml +53 -0
  93. data/lib/skr/core/testing/fixtures/skr/customers.yml +43 -0
  94. data/lib/skr/core/testing/fixtures/skr/gl_accounts.yml +86 -0
  95. data/lib/skr/core/testing/fixtures/skr/gl_manual_entries.yml +4 -0
  96. data/lib/skr/core/testing/fixtures/skr/gl_periods.yml +26 -0
  97. data/lib/skr/core/testing/fixtures/skr/gl_postings.yml +88 -0
  98. data/lib/skr/core/testing/fixtures/skr/gl_transactions.yml +35 -0
  99. data/lib/skr/core/testing/fixtures/skr/ia_lines.yml +7 -0
  100. data/lib/skr/core/testing/fixtures/skr/ia_reasons.yml +15 -0
  101. data/lib/skr/core/testing/fixtures/skr/inv_lines.yml +22 -0
  102. data/lib/skr/core/testing/fixtures/skr/inventory_adjustments.yml +6 -0
  103. data/lib/skr/core/testing/fixtures/skr/invoices.yml +10 -0
  104. data/lib/skr/core/testing/fixtures/skr/locations.yml +24 -0
  105. data/lib/skr/core/testing/fixtures/skr/payment_terms.yml +42 -0
  106. data/lib/skr/core/testing/fixtures/skr/pick_tickets.yml +4 -0
  107. data/lib/skr/core/testing/fixtures/skr/po_lines.yml +44 -0
  108. data/lib/skr/core/testing/fixtures/skr/po_receipts.yml +5 -0
  109. data/lib/skr/core/testing/fixtures/skr/por_lines.yml +13 -0
  110. data/lib/skr/core/testing/fixtures/skr/pt_lines.yml +12 -0
  111. data/lib/skr/core/testing/fixtures/skr/purchase_orders.yml +16 -0
  112. data/lib/skr/core/testing/fixtures/skr/sales_orders.yml +32 -0
  113. data/lib/skr/core/testing/fixtures/skr/sku_locs.yml +78 -0
  114. data/lib/skr/core/testing/fixtures/skr/sku_trans.yml +9 -0
  115. data/lib/skr/core/testing/fixtures/skr/sku_vendors.yml +35 -0
  116. data/lib/skr/core/testing/fixtures/skr/skus.yml +50 -0
  117. data/lib/skr/core/testing/fixtures/skr/so_lines.yml +71 -0
  118. data/lib/skr/core/testing/fixtures/skr/uoms.yml +61 -0
  119. data/lib/skr/core/testing/fixtures/skr/vendors.yml +48 -0
  120. data/lib/skr/core/testing/fixtures/skr/vo_lines.yml +12 -0
  121. data/lib/skr/core/testing/fixtures/skr/vouchers.yml +8 -0
  122. data/lib/skr/core/testing/helper.rb +18 -0
  123. data/lib/skr/core/testing/test_case.rb +17 -0
  124. data/lib/skr/core/version.rb +5 -0
  125. data/lib/skr/customer.rb +34 -0
  126. data/lib/skr/gl_account.rb +56 -0
  127. data/lib/skr/gl_manual_entry.rb +31 -0
  128. data/lib/skr/gl_period.rb +13 -0
  129. data/lib/skr/gl_posting.rb +54 -0
  130. data/lib/skr/gl_transaction.rb +174 -0
  131. data/lib/skr/ia_line.rb +129 -0
  132. data/lib/skr/ia_reason.rb +16 -0
  133. data/lib/skr/inv_line.rb +90 -0
  134. data/lib/skr/inventory_adjustment.rb +60 -0
  135. data/lib/skr/invoice.rb +159 -0
  136. data/lib/skr/location.rb +31 -0
  137. data/lib/skr/model.rb +37 -0
  138. data/lib/skr/null_user.rb +30 -0
  139. data/lib/skr/payment_term.rb +30 -0
  140. data/lib/skr/pick_ticket.rb +71 -0
  141. data/lib/skr/po_line.rb +69 -0
  142. data/lib/skr/po_receipt.rb +51 -0
  143. data/lib/skr/por_line.rb +80 -0
  144. data/lib/skr/pt_line.rb +74 -0
  145. data/lib/skr/purchase_order.rb +112 -0
  146. data/lib/skr/sales_order.rb +159 -0
  147. data/lib/skr/sequential_id.rb +23 -0
  148. data/lib/skr/sku.rb +99 -0
  149. data/lib/skr/sku_loc.rb +94 -0
  150. data/lib/skr/sku_tran.rb +111 -0
  151. data/lib/skr/sku_vendor.rb +26 -0
  152. data/lib/skr/so_line.rb +159 -0
  153. data/lib/skr/uom.rb +63 -0
  154. data/lib/skr/user_proxy.rb +60 -0
  155. data/lib/skr/validators/all.rb +2 -0
  156. data/lib/skr/validators/email.rb +17 -0
  157. data/lib/skr/validators/set.rb +18 -0
  158. data/lib/skr/vendor.rb +33 -0
  159. data/lib/skr/vo_line.rb +35 -0
  160. data/lib/skr/voucher.rb +119 -0
  161. data/lib/stockor/core.rb +9 -0
  162. data/stockor-core.gemspec +37 -0
  163. data/tasks/migrations.rake +23 -0
  164. data/tasks/publish.rake +8 -0
  165. data/test/address_test.rb +57 -0
  166. data/test/concerns/attr_with_default_test.rb +45 -0
  167. data/test/concerns/code_identifier_test.rb +46 -0
  168. data/test/concerns/export_associations_test.rb +7 -0
  169. data/test/concerns/export_methods_test.rb +31 -0
  170. data/test/concerns/export_scope_test.rb +16 -0
  171. data/test/concerns/exported_limits_test.rb +47 -0
  172. data/test/concerns/json_attribute_access_test.rb +27 -0
  173. data/test/concerns/pub_sub_test.rb +83 -0
  174. data/test/concerns/sanitize_json_test.rb +47 -0
  175. data/test/core/configuration_test.rb +24 -0
  176. data/test/core/numbers_test.rb +26 -0
  177. data/test/core/strings_test.rb +41 -0
  178. data/test/customer_test.rb +34 -0
  179. data/test/gl_account_test.rb +23 -0
  180. data/test/gl_manual_entry_test.rb +17 -0
  181. data/test/gl_period_test.rb +12 -0
  182. data/test/gl_posting_test.rb +39 -0
  183. data/test/gl_transaction_test.rb +58 -0
  184. data/test/ia_line_test.rb +68 -0
  185. data/test/ia_reason_test.rb +11 -0
  186. data/test/inv_line_test.rb +58 -0
  187. data/test/inventory_adjustment_test.rb +72 -0
  188. data/test/invoice_test.rb +63 -0
  189. data/test/location_test.rb +10 -0
  190. data/test/payment_term_test.rb +25 -0
  191. data/test/pick_ticket_test.rb +27 -0
  192. data/test/po_line_test.rb +36 -0
  193. data/test/po_receipt_test.rb +93 -0
  194. data/test/por_line_test.rb +79 -0
  195. data/test/pt_line_test.rb +29 -0
  196. data/test/purchase_order_test.rb +44 -0
  197. data/test/sales_order_test.rb +74 -0
  198. data/test/sku_loc_test.rb +50 -0
  199. data/test/sku_test.rb +45 -0
  200. data/test/sku_tran_test.rb +21 -0
  201. data/test/sku_vendor_test.rb +13 -0
  202. data/test/so_line_test.rb +89 -0
  203. data/test/test_helper.rb +1 -0
  204. data/test/uom_test.rb +14 -0
  205. data/test/vendor_test.rb +35 -0
  206. data/test/vo_line_test.rb +20 -0
  207. data/test/voucher_test.rb +35 -0
  208. data/yard_ext/all.rb +9 -0
  209. data/yard_ext/code_identifier_handler.rb +33 -0
  210. data/yard_ext/concern_meta_methods.rb +60 -0
  211. data/yard_ext/config_options.rb +27 -0
  212. data/yard_ext/exported_scope.rb +4 -0
  213. data/yard_ext/immutable_handler.rb +17 -0
  214. data/yard_ext/json_attr_accessor.rb +22 -0
  215. data/yard_ext/locked_fields_handler.rb +21 -0
  216. data/yard_ext/templates/default/layout/html/layout.erb +20 -0
  217. data/yard_ext/templates/default/method_details/html/github_link.erb +1 -0
  218. data/yard_ext/templates/default/method_details/setup.rb +3 -0
  219. data/yard_ext/validators.rb +1 -0
  220. data/yard_ext/visible_id_handler.rb +38 -0
  221. metadata +448 -0
@@ -0,0 +1,54 @@
1
+ module Skr::Concerns
2
+
3
+ # @see ClassMethods
4
+ module AttrAccessorWithDefault
5
+ extend ActiveSupport::Concern
6
+
7
+ module ClassMethods
8
+
9
+ # defines a attr_accessor with a default value
10
+ # @param name [ Symbol ] name of the attribute
11
+ # @param default [ Object, lambda, Proc ] if a value is given, be aware that it will be shared between instances
12
+ # @example
13
+ #
14
+ # Shared = Struct.new(:str)
15
+ # class AttrTestClass
16
+ # include AttrAccessorWithDefault
17
+ # attr_accessor_with_default :non_copying, ->{ Shared.new("a default string") }
18
+ # attr_accessor_with_default :shared, Shared.new("a default string")
19
+ # end
20
+ # a = AttrTestClass.new
21
+ # b = AttrTestClass.new
22
+ # a.non_copying.str #=> "a default string"
23
+ # a.non_copying.str = "new_string" #=> "new string"
24
+ # b.non_copying.str #=> "a default string"
25
+ #
26
+ # a.shared.str #=> "a default string"
27
+ # b.shared.str #=> "a default string"
28
+ # a.shared.str = "new string" #=> "new string"
29
+ # b.shared.str #=> "new string"
30
+
31
+ def attr_accessor_with_default( name, default )
32
+ attr_writer name
33
+ attr_reader_with_default( name, default )
34
+ end
35
+
36
+ def attr_reader_with_default( name, default )
37
+ module_eval do
38
+ define_method( name ) do
39
+ class << self; self; end.class_eval do
40
+ attr_reader( name )
41
+ end
42
+ if instance_variables.include? "@#{name}"
43
+ instance_variable_get( "@#{name}" )
44
+ else
45
+ instance_variable_set( "@#{name}", default.is_a?(Proc) ? default.call : default )
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+
53
+
54
+ end
@@ -0,0 +1,43 @@
1
+ module Skr
2
+ module Concerns
3
+
4
+ # @see ClassMethods
5
+ module CodeIdentifier
6
+
7
+ extend ActiveSupport::Concern
8
+
9
+ # ### Code Identifier Concern
10
+ # This adds the {#has_code_identifier} class method
11
+ module ClassMethods
12
+
13
+ # A 2-10 character string that identifies an entity, such as a Customer, Vendor, or SKU.
14
+ # The code is often assigned by the user, but may also
15
+ # be auto-generated by {Skr::Core::Strings.code_identifier}
16
+ #
17
+ # @param max_length [Integer] how long the code is allowed to be
18
+ # @param from [Symbol], method to call for a string to base the code upon
19
+ def has_code_identifier( from: nil, max_length: 10 )
20
+
21
+ validates :code, :length=>2..max_length, :presence=>true, :uniqueness=>true
22
+
23
+ alias_attribute :record_identifier, :code
24
+
25
+ if from
26
+ before_validation(:on=>:create) do
27
+ source = self[from]
28
+ unless source.blank?
29
+ self.code ||= Skr::Core::Strings.code_identifier( source, length:max_length )
30
+ end
31
+ end
32
+ end
33
+
34
+ before_validation do
35
+ self.code = self.code.upcase unless self.code.blank?
36
+ end
37
+ end
38
+
39
+ end
40
+
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,52 @@
1
+ module Skr::Concerns
2
+
3
+ # @see ClassMethods
4
+ module ExportAssociations
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ class_attribute :exported_associations
9
+ end
10
+
11
+ module ClassMethods
12
+ # Mark associations as exported, meaning they can be quered against and optionally written to
13
+ # @param associations [list of Symbols]
14
+ # @option options [Boolean] :writable should the associations accept nested attributes
15
+ # @option options [Boolean] :mandatory should the association be included with it's parent at all times?
16
+ # @option options [Symbol referring to a method, lambda] :limit restrict to Users for whom true is returned
17
+ def export_associations( *associations )
18
+ self.exported_associations ||= {}
19
+ associations.flatten!
20
+ options = associations.extract_options!
21
+ associations.each do | assoc_name |
22
+ self.exported_associations[ assoc_name.to_sym ] = options
23
+ if options[:writable]
24
+ accepts_nested_attributes_for( assoc_name, options.except(:writable, :optional) )
25
+ end
26
+ if false == options[:optional]
27
+ self.export_methods( *assoc_name, { :optional=>false })
28
+ end
29
+ end
30
+ end
31
+
32
+ # If the association is exported it may be queried against and it's data included
33
+ # along with the parent record. It may not be written to unless the :writable flag
34
+ # was set, in which case it'll be allowed by has_exported_association?
35
+ def has_exported_association?( association, user )
36
+ self.exported_associations &&
37
+ ( options = self.exported_associations[ association.to_sym ] ) &&
38
+ evaluate_export_limit( user, :association, association, options[:limit] )
39
+ end
40
+
41
+ # Does the association allow writes? True if the association has been exported
42
+ # and it accepts nested attributes
43
+ def has_exported_nested_attribute?( association, user )
44
+ self.nested_attributes_options[ association.to_sym ] &&
45
+ self.has_exported_association?( association, user )
46
+ end
47
+
48
+ end
49
+
50
+ end
51
+
52
+ end
@@ -0,0 +1,39 @@
1
+ module Skr::Concerns
2
+
3
+ module ExportJoinTables
4
+
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ class_attribute :exported_join_tables
9
+ end
10
+
11
+ module ClassMethods
12
+ # Mark a joined table as safe to be included in a query
13
+ # Primarily used for joining a model to a view for access to summarized data
14
+ def export_join_tables( *tables )
15
+ include ExportedLimitEvaluator
16
+ self.exported_join_tables ||= []
17
+ tables.flatten!
18
+ options = tables.extract_options!
19
+ tables.each do | join_name |
20
+ self.exported_join_tables << {
21
+ name: join_name,
22
+ limit: options[:limit]
23
+ }
24
+ end
25
+
26
+ end
27
+
28
+ # Has the join been marked as safe?
29
+ def has_exported_join_table?(name, user)
30
+ return true if name == 'details' # "details" is reserved for views and is always allowed
31
+ self.exported_join_tables && self.exported_join_tables.detect{ | join |
32
+ join[:name] == name && evaluate_export_limit( user, :join, join[:name], join[:limit] )
33
+ }
34
+ end
35
+
36
+ end
37
+ end
38
+
39
+ end
@@ -0,0 +1,104 @@
1
+ module Skr::Concerns
2
+
3
+ # @see ClassMethods
4
+ module ExportMethods
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+
9
+ # methods that the model has exported. Not intended for querying directly.
10
+ # The API and interested parties should call {#has_exported_method?}
11
+ class_attribute :exported_methods
12
+ end
13
+
14
+ module ClassMethods
15
+
16
+ # Called by a model to export methods to the API
17
+ # An exported method will be called and it's results returned along with the models
18
+ # JSON representation.
19
+ # @option options [Boolean] :optional if false, the method will always be called and included
20
+ # @option options [Symbol, Array of Symbols] :depends name(s) of associations that should
21
+ # be pre-loaded before calling method. Intended to prevent N+1 queries
22
+ def export_methods( *method_names )
23
+ method_names.flatten!
24
+ options = method_names.extract_options!
25
+ self.exported_methods ||= {}
26
+ method_names.map do | name |
27
+ exported_methods[ name.to_sym] = options
28
+ end
29
+ end
30
+
31
+ # Convenience method to create a Rails delegation and export the resulting method
32
+ # @example
33
+ # class Foo < Skr::Model
34
+ # belongs_to :bar
35
+ # delegate_and_export :bar_name, :optional=>false
36
+ # end
37
+ #
38
+ # foo = Foo.new
39
+ # foo.bar_name #=> calls foo.bar.name
40
+ # Foo.has_exported_method?( :bar_name ) #=> true
41
+ # skr_to_json( foo ) #=> will first load the bar association, then
42
+ # call foo.bar_name and include it's result in the JSON
43
+ def delegate_and_export( *names )
44
+ options = names.extract_options!
45
+ names.each do | name |
46
+ target,field = name.to_s.split(/_(?=[^_]+(?: |$))| /)
47
+ delegate_and_export_field( target, field, optional: options[:optional], limit: options[:limit] )
48
+ end
49
+ end
50
+
51
+ # For situations where the delegate_and_export method guesses wrong on the association and field names
52
+ # @param target [Association] association to delegate to
53
+ # @param field [Symbol] method on Association to call
54
+ # @param optional [Boolean] should the method be called and results included all the time,
55
+ # or only if specifically requested
56
+ # @param limit [Symbol referring to a method, lambda] restrict to Users for whom true is returned
57
+ # @example
58
+ # class PoLine < Skr::Model
59
+ # belongs_to :purchase_order
60
+ # delegate_and_export :purchase_order_visible_id
61
+ # end
62
+ # would generate a method ``purchase_order_visible_id`` that would call ``purchase_order_visible.id``
63
+ # This would do the right thing:
64
+ # class PoLine < Skr::Model
65
+ # belongs_to :purchase_order
66
+ # delegate_and_export_field :purchase_order, :visible_id
67
+ # end
68
+ def delegate_and_export_field( target, field, optional: true, limit: nil )
69
+ delegate field, to: target, prefix: target, allow_nil: true
70
+ self.export_methods( "#{target}_#{field}", { depends: target.to_sym,
71
+ optional: optional,
72
+ limit: limit } )
73
+ end
74
+
75
+ # Check if the method can be called by user
76
+ def has_exported_method?( name, user )
77
+ if self.exported_methods && ( method_options = self.exported_methods[ name.to_sym ] )
78
+ return evaluate_export_limit( user, :method, name, method_options[:limit] )
79
+ else
80
+ return false
81
+ end
82
+ end
83
+
84
+ # Retrieve the list of dependent associations for the given methods
85
+ # Also includes methods that were exported as ``optional: false``
86
+ # @param requested_methods [Array] methods that the user has requested be executed and added to the JSON payload
87
+ # @return [Array] list of associations that should be included in query
88
+ def exported_method_dependancies( requested_methods )
89
+ requested_methods.map!(&:to_sym)
90
+ return [] if self.exported_methods.blank?
91
+ depends = self.exported_methods.each_with_object(Array.new) do | kv, result |
92
+ ( export, options ) = kv
93
+ if options[:depends] && ( false == options[:optional] || requested_methods.include?(export) )
94
+ result << options[:depends]
95
+ end
96
+ end
97
+ depends.uniq
98
+ end
99
+ end
100
+
101
+
102
+ end
103
+
104
+ end
@@ -0,0 +1,66 @@
1
+ require_relative 'exported_limit_evaluator'
2
+
3
+ module Skr::Concerns
4
+
5
+ # @see ClassMethods
6
+ module ExportScope
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ # scopes that the model has exported. Not intended for querying directly.
11
+ # The API and interested parties should call {#has_exported_scope?}
12
+ class_attribute :exported_scopes
13
+ extend ExportedLimitEvaluator
14
+ end
15
+
16
+ # ### Mark a scope as "exportable"
17
+ #
18
+ # An exported scope is safe for querying by external clients over the API.
19
+ # The scope should always:
20
+ #
21
+ # * Safely escape data *(should __ALWAYS__ do this anyway, but it bears mentioning again)*
22
+ # * Be relatively simple and complete quickly.
23
+ # * Provide value to the client that it cannot obtain by using normal query methods
24
+ module ClassMethods
25
+ def scope(name, body, options = {}, &block)
26
+ super(name, body, &block)
27
+ if (export = options[:export])
28
+ export_scope(name, body, limit: (export == true ? nil : export[:limit]))
29
+ end
30
+ end
31
+
32
+ # Mark scope as query-able by the API.
33
+ # @param name [Symbol,String] Rails will create a class method with this name
34
+ # @param query [lambda] Arel query. This is passed off to Rail's for setting up the scope.
35
+ # @param limit [Symbol referring to a Class method name, lambda]
36
+ # If given, this will be queried by the API to determining if a given user may call the scope
37
+ # @return nil
38
+ def export_scope(name, query, limit: nil)
39
+ include ExportedLimitEvaluator
40
+
41
+ self.exported_scopes ||= Hash.new
42
+ self.exported_scopes[name.to_sym] = {
43
+ scope: scope(name, query),
44
+ name: name,
45
+ limit: limit
46
+ }
47
+ nil
48
+ end
49
+
50
+ # The api can query this to determine if the scope is safe to be called
51
+ # from the API by [user]
52
+ # @param name [Symbol,String] name of scope
53
+ # @param user [User] who is performing the request.
54
+ # This is passed off to the method or lambda that was given as the limit argument in {#export_scope}
55
+
56
+ def has_exported_scope?(name, user)
57
+ if self.exported_scopes && ( scope_options = self.exported_scopes[ name.to_sym ] )
58
+ return evaluate_export_limit( user, :scope, name, scope_options[:limit] )
59
+ else
60
+ return false
61
+ end
62
+ end
63
+
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,17 @@
1
+ module Skr::Concerns
2
+
3
+ module ExportedLimitEvaluator
4
+
5
+ def evaluate_export_limit( user, type, name, limit )
6
+ if limit.nil?
7
+ true
8
+ elsif limit.is_a?(Symbol)
9
+ self.send( limit, user, type, name )
10
+ else
11
+ limit.call( user, type, name )
12
+ end
13
+ end
14
+
15
+ end
16
+
17
+ end
@@ -0,0 +1,18 @@
1
+ module Skr
2
+ module Concerns
3
+
4
+ module GlTran
5
+
6
+ module Postings
7
+
8
+ def total
9
+ total = BigDecimal.new('0')
10
+ each{ |pst| total+=pst.amount }
11
+ total
12
+ end
13
+
14
+ end
15
+
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,67 @@
1
+ require 'active_record/validations'
2
+
3
+ module Skr
4
+
5
+ class InvalidGlTransaction < ActiveRecord::RecordInvalid
6
+ end
7
+
8
+
9
+ module Concerns
10
+
11
+ module HasGlTransaction
12
+
13
+ extend ActiveSupport::Concern
14
+
15
+ module InstanceMethods
16
+
17
+ # N.B. We are overriding ActiveRecord::Base#save
18
+ # This is so we can wrap the save call inside a {GlTransaction.record} block
19
+ # which will collect any GlPostings and then collapse them down at the end of the block
20
+ # in order to prevent multiple postings to the same account.
21
+ def save(*)
22
+ opts = options_for_gl_transaction
23
+ # do nothing if the options specified an if clause and the method returns false
24
+ if opts.present? and opts.has_key?(:if) and self.send( opts[:if] ) == false
25
+ super
26
+ else
27
+ save_status = false
28
+ # First we wrap it up in our transaction since we don't have the benefit of
29
+ # save's rollback_active_record_state! block
30
+ # I'm tempted to appropriate it's usage, but am not since it's not documented (that I've found)
31
+ # and is thus likely to change
32
+ GlTransaction.transaction( requires_new: true ) do
33
+ # Enclose in GlTransaction#record block
34
+ gl_transaction = GlTransaction.record do
35
+ save_status = super # ActiveRecord::Base#save
36
+ # If save was successfull, pass attributes back to GlTransaction#record.
37
+ # Otherwise pass false back so it knows to not save the GlTransaction
38
+ save_status ? { attributes: self.send( opts[:attributes] ) } : false
39
+ end
40
+ # if we saved successfully, but the GlTransaction did not;
41
+ # Copy the errors to the model and abort the transaction
42
+ if save_status && gl_transaction.errors.any?
43
+ self.errors[:gl_transaction] += gl_transaction.errors.full_messages
44
+ save_status = false
45
+ raise InvalidGlTransaction.new(gl_transaction)
46
+ end
47
+ end
48
+ return save_status
49
+ end
50
+ end
51
+
52
+ end
53
+
54
+ module ClassMethods
55
+
56
+ def has_gl_transaction( options={} )
57
+ options.merge!( attributes: :attributes_for_gl_transaction )
58
+ cattr_accessor :options_for_gl_transaction
59
+ self.options_for_gl_transaction = options unless options.empty?
60
+ self.send :include, InstanceMethods
61
+ end
62
+
63
+ end
64
+
65
+ end
66
+ end
67
+ end