brisk-bills 0.7.0 → 0.8.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (396) hide show
  1. data/CHANGELOG +7 -0
  2. data/Gemfile +11 -0
  3. data/Gemfile.lock +55 -0
  4. data/TODO.txt +36 -67
  5. data/app/controllers/admin/activities_controller.rb +4 -3
  6. data/app/controllers/admin/activities_with_prices_controller.rb +28 -14
  7. data/app/controllers/admin/client_accounting_controller.rb +11 -8
  8. data/app/controllers/admin/client_representatives_controller.rb +34 -5
  9. data/app/controllers/admin/clients_controller.rb +1 -1
  10. data/app/controllers/admin/draft_invoices_controller.rb +92 -0
  11. data/app/controllers/admin/{employee_client_labor_rate_controller.rb → employee_client_labor_rates_controller.rb} +1 -1
  12. data/app/controllers/admin/employees_controller.rb +3 -4
  13. data/app/controllers/admin/invoices_controller.rb +111 -53
  14. data/app/controllers/admin/payments_controller.rb +168 -41
  15. data/app/controllers/authentication_controller.rb +3 -3
  16. data/app/helpers/admin/activities_helper.rb +16 -13
  17. data/app/helpers/admin/activities_helper/labor_helper.rb +4 -4
  18. data/app/helpers/admin/activities_helper/proposal_helper.rb +1 -1
  19. data/app/helpers/admin/activities_helper/slimtimer_helper.rb +1 -1
  20. data/app/helpers/admin/activities_with_prices_helper.rb +2 -2
  21. data/app/helpers/admin/activity_type_controller_helper.rb +6 -0
  22. data/app/helpers/admin/activity_type_field_helper.rb +9 -9
  23. data/app/helpers/admin/adjustments_helper.rb +9 -1
  24. data/app/helpers/admin/client_accounting_helper.rb +2 -2
  25. data/app/helpers/admin/client_financial_transactions_helper.rb +1 -1
  26. data/app/helpers/admin/client_representatives_helper.rb +16 -12
  27. data/app/helpers/admin/clients_helper.rb +2 -0
  28. data/app/helpers/admin/draft_invoices_helper.rb +24 -0
  29. data/app/helpers/admin/{employee_client_labor_rate_helper.rb → employee_client_labor_rates_helper.rb} +8 -8
  30. data/app/helpers/admin/employees_helper.rb +6 -0
  31. data/app/helpers/admin/has_credential_column_helper.rb +8 -8
  32. data/app/helpers/admin/invoices_helper.rb +71 -16
  33. data/app/helpers/admin/is_active_column_helper.rb +2 -2
  34. data/app/helpers/admin/labors_helper.rb +12 -1
  35. data/app/helpers/admin/labors_helper/slimtimer_helper.rb +1 -1
  36. data/app/helpers/admin/materials_helper.rb +11 -0
  37. data/app/helpers/admin/payments_helper.rb +204 -29
  38. data/app/helpers/admin/proposals_helper.rb +10 -0
  39. data/app/helpers/admin/settings_helper.rb +1 -1
  40. data/app/helpers/admin_layout_helper.rb +3 -6
  41. data/app/helpers/authentication_helper.rb +7 -10
  42. data/app/helpers/money_model_helper.rb +1 -2
  43. data/app/models/activity.rb +17 -7
  44. data/app/models/client.rb +35 -8
  45. data/app/{model_views → models}/client_financial_transaction.rb +0 -0
  46. data/app/models/client_representative.rb +6 -14
  47. data/app/models/credential.rb +1 -1
  48. data/app/models/employee.rb +9 -11
  49. data/app/models/invoice.rb +13 -9
  50. data/app/models/invoice_payment.rb +1 -1
  51. data/app/models/notifier.rb +2 -2
  52. data/app/models/payment.rb +51 -7
  53. data/app/models/setting.rb +3 -1
  54. data/app/views/active_scaffold_overrides/add_existing.js.rjs +25 -0
  55. data/app/views/admin/activities/_adjustment_column.html.erb +10 -0
  56. data/app/views/admin/activities/{_form.rhtml → _form.html.erb} +0 -0
  57. data/app/views/admin/activities/{_form_attribute.rhtml → _form_attribute.html.erb} +0 -0
  58. data/app/views/admin/activities/{_labor_column.rhtml → _labor_column.html.erb} +0 -0
  59. data/app/views/admin/activities/{_material_column.rhtml → _material_column.html.erb} +0 -0
  60. data/app/views/admin/activities/{_proposal_column.rhtml → _proposal_column.html.erb} +0 -0
  61. data/app/views/admin/activities_with_prices/{move_to_invoice.rhtml → move_to_invoice.html.erb} +2 -2
  62. data/app/views/admin/activities_with_prices/move_to_invoice.js.rjs +5 -0
  63. data/app/views/admin/draft_invoices/batch_create.html.erb +73 -0
  64. data/app/views/admin/draft_invoices/batch_create.js.rjs +5 -0
  65. data/app/views/admin/invoices/confirm_publish_modal.html.erb +48 -0
  66. data/app/views/admin/payments/commit_payment_warning.html.erb +8 -0
  67. data/app/views/admin/payments/observation_error.js.rjs +28 -0
  68. data/app/views/admin/payments/on_assignment_observation.js.rjs +28 -0
  69. data/app/views/authentication/{email.rjs → email.js.rjs} +0 -0
  70. data/app/views/authentication/{login.rhtml → login.html.erb} +21 -26
  71. data/app/views/authentication/{login.rjs → login.js.rjs} +0 -0
  72. data/app/views/authentication/{reset_password_via_token.rhtml → reset_password_via_token.html.erb} +0 -0
  73. data/app/views/authentication/{reset_password_via_token.rjs → reset_password_via_token.js.rjs} +0 -0
  74. data/app/views/authentication/{sign_in_error.rjs → sign_in_error.js.rjs} +0 -0
  75. data/app/views/layouts/{_navigation_tree.rhtml → _navigation_tree.html.erb} +0 -0
  76. data/app/views/layouts/{admin.rhtml → admin.html.erb} +0 -0
  77. data/app/views/layouts/{public.rhtml → public.html.erb} +0 -0
  78. data/app/views/notifier/{_email_footer.html.rhtml → _email_footer.html.erb} +0 -0
  79. data/app/views/notifier/{_email_footer.plain.rhtml → _email_footer.plain.erb} +0 -0
  80. data/app/views/notifier/{_email_header.html.rhtml → _email_header.html.erb} +0 -0
  81. data/app/views/notifier/invoice_available.html.erb +5 -0
  82. data/app/views/notifier/{invoice_available.plain.rhtml → invoice_available.plain.erb} +1 -1
  83. data/app/views/notifier/{reset_password_requested.html.rhtml → reset_password_requested.html.erb} +2 -2
  84. data/app/views/notifier/{reset_password_requested.plain.rhtml → reset_password_requested.plain.erb} +1 -1
  85. data/config/environment.rb +6 -5
  86. data/config/locale/en.rb +9 -0
  87. data/db/migrate/001_create_employees.rb +1 -1
  88. data/db/migrate/002_create_employee_slimtimers.rb +1 -1
  89. data/db/migrate/003_create_slimtimer_tasks.rb +1 -1
  90. data/db/migrate/004_create_slimtimer_time_entries.rb +1 -1
  91. data/db/migrate/005_create_clients.rb +1 -1
  92. data/db/migrate/006_create_client_representatives.rb +1 -1
  93. data/db/migrate/008_create_activities.rb +1 -1
  94. data/db/migrate/009_create_activity_labors.rb +1 -1
  95. data/db/migrate/010_create_employee_client_labor_rates.rb +1 -1
  96. data/db/migrate/011_create_activity_adjustments.rb +1 -1
  97. data/db/migrate/012_create_activity_materials.rb +1 -1
  98. data/db/migrate/013_create_activity_proposals.rb +1 -1
  99. data/db/migrate/014_create_invoices.rb +1 -1
  100. data/db/migrate/015_create_payments.rb +1 -1
  101. data/db/migrate/016_create_payment_methods.rb +1 -1
  102. data/db/migrate/017_create_invoice_payments.rb +1 -1
  103. data/db/migrate/018_create_activity_types.rb +2 -2
  104. data/db/migrate/019_create_settings.rb +1 -1
  105. data/db/migrate/023_create_credentials_migrate_representatives.rb +2 -2
  106. data/db/migrate/028_money_to_cents.rb +2 -2
  107. data/db/schema.rb +10 -10
  108. data/lib/brisk-bills.rb +2 -2
  109. data/lib/brisk-bills/initializer.rb +2 -2
  110. data/lib/generators/instance/templates/instance_environment.rb +1 -1
  111. data/lib/libpptable.rb +48 -0
  112. data/lib/tasks/create_last_months_invoices.rake +1 -14
  113. data/lib/tasks/first_time_setup.rake +2 -2
  114. data/lib/tasks/package.rake +2 -1
  115. data/lib/tasks/payment_assignment_consistency_check.rake +110 -0
  116. data/lib/utilities.rb +4 -4
  117. data/public/images/page-new.gif +0 -0
  118. data/public/javascripts/active_scaffold/default/active_scaffold.js +532 -430
  119. data/public/javascripts/active_scaffold/default/dhtml_history.js +1 -1
  120. data/public/javascripts/active_scaffold/default/form_enhancements.js +7 -4
  121. data/public/stylesheets/active_scaffold/default/stylesheet-ie.css +5 -5
  122. data/public/stylesheets/active_scaffold/default/stylesheet.css +54 -18
  123. data/public/stylesheets/admin/global.css +1 -1
  124. data/public/stylesheets/admin/pages.css +29 -0
  125. data/test/unit/activity/adjustment_test.rb +1 -1
  126. data/test/unit/activity/labor_test.rb +1 -1
  127. data/test/unit/activity/material_test.rb +1 -1
  128. data/test/unit/activity/proposal_test.rb +1 -1
  129. data/test/unit/activity_test.rb +1 -1
  130. data/test/unit/activity_type_test.rb +1 -1
  131. data/test/unit/client_eventlog_test.rb +1 -1
  132. data/test/unit/client_financial_transaction_test.rb +1 -1
  133. data/test/unit/client_representative_test.rb +1 -1
  134. data/test/unit/client_test.rb +37 -6
  135. data/test/unit/credential_test.rb +1 -1
  136. data/test/unit/employee/slimtimer_test.rb +1 -1
  137. data/test/unit/employee_client_labor_rate_test.rb +1 -1
  138. data/test/unit/employee_test.rb +1 -1
  139. data/test/unit/helpers/admin/draft_invoices_helper_test.rb +4 -0
  140. data/test/unit/invoice_payment_test.rb +90 -50
  141. data/test/unit/invoice_test.rb +48 -8
  142. data/test/unit/notifier_test.rb +1 -1
  143. data/test/unit/payment_method_test.rb +1 -1
  144. data/test/unit/payment_test.rb +7 -7
  145. data/test/unit/setting_test.rb +1 -1
  146. data/test/unit/slimtimer_task_test.rb +1 -1
  147. data/test/unit/slimtimer_time_entry_test.rb +1 -1
  148. data/vendor/plugins/active_scaffold/CHANGELOG +3 -1
  149. data/vendor/plugins/active_scaffold/README +18 -5
  150. data/vendor/plugins/active_scaffold/environment.rb +1 -1
  151. data/vendor/plugins/active_scaffold/frontends/default/javascripts/active_scaffold.js +532 -430
  152. data/vendor/plugins/active_scaffold/frontends/default/javascripts/dhtml_history.js +1 -1
  153. data/vendor/plugins/active_scaffold/frontends/default/javascripts/form_enhancements.js +7 -4
  154. data/vendor/plugins/active_scaffold/frontends/default/stylesheets/stylesheet-ie.css +5 -5
  155. data/vendor/plugins/active_scaffold/frontends/default/stylesheets/stylesheet.css +54 -18
  156. data/vendor/plugins/active_scaffold/frontends/default/views/_add_existing_form.html.erb +3 -7
  157. data/vendor/plugins/active_scaffold/frontends/default/views/_create_form.html.erb +10 -7
  158. data/vendor/plugins/active_scaffold/frontends/default/views/_field_search.html.erb +7 -12
  159. data/vendor/plugins/active_scaffold/frontends/default/views/_form.html.erb +3 -3
  160. data/vendor/plugins/active_scaffold/frontends/default/views/_form_association.html.erb +15 -20
  161. data/vendor/plugins/active_scaffold/frontends/default/views/_form_association_footer.html.erb +8 -10
  162. data/vendor/plugins/active_scaffold/frontends/default/views/_form_attribute.html.erb +5 -2
  163. data/vendor/plugins/active_scaffold/frontends/default/views/_form_hidden_attribute.html.erb +2 -1
  164. data/vendor/plugins/active_scaffold/frontends/default/views/_horizontal_subform.html.erb +19 -21
  165. data/vendor/plugins/active_scaffold/frontends/default/views/{_form_association_header.html.erb → _horizontal_subform_header.html.erb} +2 -2
  166. data/vendor/plugins/active_scaffold/frontends/default/views/_horizontal_subform_record.html.erb +31 -0
  167. data/vendor/plugins/active_scaffold/frontends/default/views/_list.html.erb +4 -18
  168. data/vendor/plugins/active_scaffold/frontends/default/views/_list_actions.html.erb +18 -5
  169. data/vendor/plugins/active_scaffold/frontends/default/views/_list_calculations.html.erb +2 -9
  170. data/vendor/plugins/active_scaffold/frontends/default/views/_list_column_headings.html.erb +4 -4
  171. data/vendor/plugins/active_scaffold/frontends/default/views/_list_header.html.erb +6 -6
  172. data/vendor/plugins/active_scaffold/frontends/default/views/_list_inline_adapter.html.erb +2 -1
  173. data/vendor/plugins/active_scaffold/frontends/default/views/_list_messages.html.erb +20 -0
  174. data/vendor/plugins/active_scaffold/frontends/default/views/_list_pagination.html.erb +11 -0
  175. data/vendor/plugins/active_scaffold/frontends/default/views/_list_pagination_links.html.erb +3 -5
  176. data/vendor/plugins/active_scaffold/frontends/default/views/_list_record.html.erb +2 -26
  177. data/vendor/plugins/active_scaffold/frontends/default/views/_list_record_columns.html.erb +9 -0
  178. data/vendor/plugins/active_scaffold/frontends/default/views/_nested.html.erb +3 -12
  179. data/vendor/plugins/active_scaffold/frontends/default/views/_render_fields.js.rjs +11 -0
  180. data/vendor/plugins/active_scaffold/frontends/default/views/_search.html.erb +17 -11
  181. data/vendor/plugins/active_scaffold/frontends/default/views/_show.html.erb +3 -3
  182. data/vendor/plugins/active_scaffold/frontends/default/views/_show_columns.html.erb +8 -7
  183. data/vendor/plugins/active_scaffold/frontends/default/views/_update_actions.html.erb +3 -3
  184. data/vendor/plugins/active_scaffold/frontends/default/views/_update_form.html.erb +9 -6
  185. data/vendor/plugins/active_scaffold/frontends/default/views/_vertical_subform.html.erb +7 -0
  186. data/vendor/plugins/active_scaffold/frontends/default/views/_vertical_subform_record.html.erb +38 -0
  187. data/vendor/plugins/active_scaffold/frontends/default/views/add_existing.js.rjs +2 -1
  188. data/vendor/plugins/active_scaffold/frontends/default/views/delete.html.erb +1 -1
  189. data/vendor/plugins/active_scaffold/frontends/default/views/destroy.js.rjs +2 -2
  190. data/vendor/plugins/active_scaffold/frontends/default/views/edit_associated.js.rjs +20 -15
  191. data/vendor/plugins/active_scaffold/frontends/default/views/list.html.erb +12 -8
  192. data/vendor/plugins/active_scaffold/frontends/default/views/list.js.rjs +1 -0
  193. data/vendor/plugins/active_scaffold/frontends/default/views/mark.js.rjs +6 -0
  194. data/vendor/plugins/active_scaffold/frontends/default/views/on_create.js.rjs +12 -8
  195. data/vendor/plugins/active_scaffold/frontends/default/views/on_update.js.rjs +13 -6
  196. data/vendor/plugins/active_scaffold/frontends/default/views/render_field.js.rjs +1 -0
  197. data/vendor/plugins/active_scaffold/frontends/default/views/search.html.erb +2 -2
  198. data/vendor/plugins/active_scaffold/frontends/default/views/update.html.erb +2 -2
  199. data/vendor/plugins/active_scaffold/frontends/default/views/update_column.js.rjs +4 -3
  200. data/vendor/plugins/active_scaffold/frontends/default/views/update_row.js.rjs +2 -0
  201. data/vendor/plugins/active_scaffold/init.rb +2 -2
  202. data/vendor/plugins/active_scaffold/install.rb +1 -2
  203. data/vendor/plugins/active_scaffold/install_assets.rb +1 -1
  204. data/vendor/plugins/active_scaffold/lib/active_record_permissions.rb +53 -42
  205. data/vendor/plugins/active_scaffold/lib/active_scaffold.rb +96 -23
  206. data/vendor/plugins/active_scaffold/lib/active_scaffold/actions/common_search.rb +18 -0
  207. data/vendor/plugins/active_scaffold/lib/active_scaffold/actions/core.rb +152 -102
  208. data/vendor/plugins/active_scaffold/lib/active_scaffold/actions/create.rb +81 -49
  209. data/vendor/plugins/active_scaffold/lib/active_scaffold/actions/delete.rb +44 -17
  210. data/vendor/plugins/active_scaffold/lib/active_scaffold/actions/field_search.rb +60 -46
  211. data/vendor/plugins/active_scaffold/lib/active_scaffold/actions/list.rb +42 -37
  212. data/vendor/plugins/active_scaffold/lib/active_scaffold/actions/mark.rb +72 -0
  213. data/vendor/plugins/active_scaffold/lib/active_scaffold/actions/nested.rb +94 -92
  214. data/vendor/plugins/active_scaffold/lib/active_scaffold/actions/search.rb +54 -40
  215. data/vendor/plugins/active_scaffold/lib/active_scaffold/actions/show.rb +30 -10
  216. data/vendor/plugins/active_scaffold/lib/active_scaffold/actions/update.rb +62 -47
  217. data/vendor/plugins/active_scaffold/lib/active_scaffold/attribute_params.rb +187 -185
  218. data/vendor/plugins/active_scaffold/lib/active_scaffold/config/base.rb +28 -3
  219. data/vendor/plugins/active_scaffold/lib/active_scaffold/config/core.rb +43 -3
  220. data/vendor/plugins/active_scaffold/lib/active_scaffold/config/create.rb +8 -15
  221. data/vendor/plugins/active_scaffold/lib/active_scaffold/config/delete.rb +3 -4
  222. data/vendor/plugins/active_scaffold/lib/active_scaffold/config/field_search.rb +19 -17
  223. data/vendor/plugins/active_scaffold/lib/active_scaffold/config/form.rb +2 -9
  224. data/vendor/plugins/active_scaffold/lib/active_scaffold/config/list.rb +46 -9
  225. data/vendor/plugins/active_scaffold/lib/active_scaffold/config/nested.rb +17 -7
  226. data/vendor/plugins/active_scaffold/lib/active_scaffold/config/search.rb +36 -16
  227. data/vendor/plugins/active_scaffold/lib/active_scaffold/config/show.rb +5 -12
  228. data/vendor/plugins/active_scaffold/lib/active_scaffold/config/subform.rb +8 -6
  229. data/vendor/plugins/active_scaffold/lib/active_scaffold/config/update.rb +12 -8
  230. data/vendor/plugins/active_scaffold/lib/active_scaffold/configurable.rb +2 -2
  231. data/vendor/plugins/active_scaffold/lib/active_scaffold/constraints.rb +173 -178
  232. data/vendor/plugins/active_scaffold/lib/active_scaffold/data_structures/action_columns.rb +101 -97
  233. data/vendor/plugins/active_scaffold/lib/active_scaffold/data_structures/action_link.rb +29 -19
  234. data/vendor/plugins/active_scaffold/lib/active_scaffold/data_structures/column.rb +74 -34
  235. data/vendor/plugins/active_scaffold/lib/active_scaffold/data_structures/columns.rb +74 -74
  236. data/vendor/plugins/active_scaffold/lib/active_scaffold/data_structures/set.rb +57 -62
  237. data/vendor/plugins/active_scaffold/lib/active_scaffold/data_structures/sorting.rb +73 -3
  238. data/vendor/plugins/active_scaffold/lib/active_scaffold/finder.rb +304 -234
  239. data/vendor/plugins/active_scaffold/lib/active_scaffold/helpers/association_helpers.rb +40 -40
  240. data/vendor/plugins/active_scaffold/lib/active_scaffold/helpers/controller_helpers.rb +39 -24
  241. data/vendor/plugins/active_scaffold/lib/active_scaffold/helpers/country_helpers.rb +32 -28
  242. data/vendor/plugins/active_scaffold/lib/active_scaffold/helpers/form_column_helpers.rb +157 -86
  243. data/vendor/plugins/active_scaffold/lib/active_scaffold/helpers/id_helpers.rb +7 -7
  244. data/vendor/plugins/active_scaffold/lib/active_scaffold/helpers/list_column_helpers.rb +278 -85
  245. data/vendor/plugins/active_scaffold/lib/active_scaffold/helpers/pagination_helpers.rb +38 -12
  246. data/vendor/plugins/active_scaffold/lib/active_scaffold/helpers/search_column_helpers.rb +74 -35
  247. data/vendor/plugins/active_scaffold/lib/active_scaffold/helpers/show_column_helpers.rb +53 -46
  248. data/vendor/plugins/active_scaffold/lib/active_scaffold/helpers/view_helpers.rb +51 -17
  249. data/vendor/plugins/active_scaffold/lib/active_scaffold/locale/de.yml +69 -0
  250. data/vendor/plugins/active_scaffold/lib/active_scaffold/locale/en.yml +72 -0
  251. data/vendor/plugins/active_scaffold/lib/active_scaffold/locale/es.yml +23 -13
  252. data/vendor/plugins/active_scaffold/lib/active_scaffold/locale/fr.yml +68 -0
  253. data/vendor/plugins/active_scaffold/lib/active_scaffold/locale/hu.yml +24 -14
  254. data/vendor/plugins/active_scaffold/lib/active_scaffold/locale/ja.yml +69 -0
  255. data/vendor/plugins/active_scaffold/lib/active_scaffold/locale/ru.yml +72 -0
  256. data/vendor/plugins/active_scaffold/lib/active_scaffold/marked_model.rb +38 -0
  257. data/vendor/plugins/active_scaffold/lib/bridges/calendar_date_select/lib/as_cds_bridge.rb +47 -13
  258. data/vendor/plugins/active_scaffold/lib/bridges/dependent_protect/bridge.rb +10 -0
  259. data/vendor/plugins/active_scaffold/lib/bridges/dependent_protect/lib/dependent_protect_bridge.rb +11 -0
  260. data/vendor/plugins/active_scaffold/lib/bridges/file_column/lib/as_file_column_bridge.rb +3 -6
  261. data/vendor/plugins/active_scaffold/lib/bridges/file_column/lib/form_ui.rb +10 -2
  262. data/vendor/plugins/active_scaffold/lib/bridges/paperclip/bridge.rb +13 -0
  263. data/vendor/plugins/active_scaffold/lib/bridges/paperclip/lib/form_ui.rb +20 -0
  264. data/vendor/plugins/active_scaffold/lib/bridges/paperclip/lib/list_ui.rb +16 -0
  265. data/vendor/plugins/active_scaffold/lib/bridges/paperclip/lib/paperclip_bridge.rb +32 -0
  266. data/vendor/plugins/active_scaffold/lib/bridges/paperclip/lib/paperclip_bridge_helpers.rb +18 -0
  267. data/vendor/plugins/active_scaffold/lib/bridges/record_select/bridge.rb +5 -0
  268. data/vendor/plugins/active_scaffold/lib/bridges/record_select/lib/record_select_bridge.rb +79 -0
  269. data/vendor/plugins/active_scaffold/lib/bridges/semantic_attributes/bridge.rb +5 -0
  270. data/vendor/plugins/active_scaffold/lib/bridges/semantic_attributes/lib/semantic_attributes_bridge.rb +20 -0
  271. data/vendor/plugins/active_scaffold/lib/bridges/tiny_mce/bridge.rb +5 -0
  272. data/vendor/plugins/active_scaffold/lib/bridges/tiny_mce/lib/tiny_mce_bridge.rb +57 -0
  273. data/vendor/plugins/active_scaffold/lib/bridges/unobtrusive_date_picker/bridge.rb +9 -0
  274. data/vendor/plugins/active_scaffold/lib/bridges/unobtrusive_date_picker/lib/form_ui.rb +14 -0
  275. data/vendor/plugins/active_scaffold/lib/bridges/unobtrusive_date_picker/lib/unobtrusive_date_picker_bridge.rb +15 -0
  276. data/vendor/plugins/active_scaffold/lib/bridges/unobtrusive_date_picker/lib/view_helpers.rb +16 -0
  277. data/vendor/plugins/active_scaffold/lib/bridges/validation_reflection/bridge.rb +9 -0
  278. data/vendor/plugins/active_scaffold/lib/bridges/validation_reflection/lib/validation_reflection_bridge.rb +19 -0
  279. data/vendor/plugins/active_scaffold/lib/extensions/action_view_rendering.rb +20 -8
  280. data/vendor/plugins/active_scaffold/lib/extensions/generic_view_paths.rb +2 -2
  281. data/vendor/plugins/active_scaffold/lib/extensions/paginator_extensions.rb +26 -0
  282. data/vendor/plugins/active_scaffold/lib/extensions/resources.rb +6 -5
  283. data/vendor/plugins/active_scaffold/lib/extensions/reverse_associations.rb +26 -20
  284. data/vendor/plugins/active_scaffold/lib/extensions/unsaved_associated.rb +1 -1
  285. data/vendor/plugins/active_scaffold/lib/paginator.rb +1 -1
  286. data/vendor/plugins/active_scaffold/lib/responds_to_parent.rb +1 -1
  287. data/vendor/plugins/active_scaffold/shoulda_macros/macros.rb +136 -0
  288. data/vendor/plugins/active_scaffold/test/bridges/active_scaffold_dependent_protect_test.rb +34 -0
  289. data/vendor/plugins/active_scaffold/test/bridges/bridge_test.rb +43 -0
  290. data/vendor/plugins/active_scaffold/test/bridges/company.rb +81 -0
  291. data/vendor/plugins/active_scaffold/test/bridges/paperclip_test.rb +68 -0
  292. data/vendor/plugins/active_scaffold/test/bridges/tiny_mce_test.rb +27 -0
  293. data/vendor/plugins/active_scaffold/test/bridges/unobtrusive_date_picker_test.rb +49 -0
  294. data/vendor/plugins/active_scaffold/test/bridges/validation_reflection_test.rb +57 -0
  295. data/vendor/plugins/active_scaffold/test/config/base_test.rb +15 -0
  296. data/vendor/plugins/active_scaffold/test/config/core_test.rb +58 -0
  297. data/vendor/plugins/active_scaffold/test/config/create_test.rb +9 -6
  298. data/vendor/plugins/active_scaffold/test/config/delete_test.rb +33 -0
  299. data/vendor/plugins/active_scaffold/test/config/field_search_test.rb +47 -0
  300. data/vendor/plugins/active_scaffold/test/config/list_test.rb +66 -11
  301. data/vendor/plugins/active_scaffold/test/config/nested_test.rb +62 -0
  302. data/vendor/plugins/active_scaffold/test/config/search_test.rb +60 -0
  303. data/vendor/plugins/active_scaffold/test/config/show_test.rb +43 -0
  304. data/vendor/plugins/active_scaffold/test/config/subform_test.rb +17 -0
  305. data/vendor/plugins/active_scaffold/test/config/update_test.rb +27 -4
  306. data/vendor/plugins/active_scaffold/test/data_structures/action_columns_test.rb +2 -8
  307. data/vendor/plugins/active_scaffold/test/data_structures/action_link_test.rb +11 -11
  308. data/vendor/plugins/active_scaffold/test/data_structures/action_links_test.rb +5 -5
  309. data/vendor/plugins/active_scaffold/test/data_structures/association_column_test.rb +4 -3
  310. data/vendor/plugins/active_scaffold/test/data_structures/column_test.rb +29 -5
  311. data/vendor/plugins/active_scaffold/test/data_structures/sorting_test.rb +31 -1
  312. data/vendor/plugins/active_scaffold/test/data_structures/standard_column_test.rb +3 -13
  313. data/vendor/plugins/active_scaffold/test/data_structures/virtual_column_test.rb +0 -11
  314. data/vendor/plugins/active_scaffold/test/extensions/{array.rb → array_test.rb} +0 -0
  315. data/vendor/plugins/active_scaffold/test/helpers/form_column_helpers_test.rb +31 -0
  316. data/vendor/plugins/active_scaffold/test/helpers/list_column_helpers_test.rb +42 -0
  317. data/vendor/plugins/active_scaffold/test/helpers/pagination_helpers_test.rb +59 -0
  318. data/vendor/plugins/active_scaffold/test/misc/active_record_permissions_test.rb +154 -0
  319. data/vendor/plugins/active_scaffold/test/misc/attribute_params_test.rb +146 -0
  320. data/vendor/plugins/active_scaffold/test/misc/configurable_test.rb +2 -2
  321. data/vendor/plugins/active_scaffold/test/misc/constraints_test.rb +26 -8
  322. data/vendor/plugins/active_scaffold/test/misc/finder_test.rb +41 -19
  323. data/vendor/plugins/active_scaffold/test/misc/lang_test.rb +5 -6
  324. data/vendor/plugins/active_scaffold/test/mock_app/app/controllers/application_controller.rb +10 -0
  325. data/vendor/plugins/active_scaffold/test/mock_app/app/helpers/application_helper.rb +3 -0
  326. data/vendor/plugins/active_scaffold/test/mock_app/config/boot.rb +110 -0
  327. data/vendor/plugins/active_scaffold/test/mock_app/config/environment.rb +43 -0
  328. data/vendor/plugins/active_scaffold/test/mock_app/config/environments/development.rb +17 -0
  329. data/vendor/plugins/active_scaffold/test/mock_app/config/environments/production.rb +28 -0
  330. data/vendor/plugins/active_scaffold/test/mock_app/config/environments/test.rb +28 -0
  331. data/vendor/plugins/active_scaffold/test/mock_app/config/initializers/backtrace_silencers.rb +7 -0
  332. data/vendor/plugins/active_scaffold/test/mock_app/config/initializers/inflections.rb +10 -0
  333. data/vendor/plugins/active_scaffold/test/mock_app/config/initializers/mime_types.rb +5 -0
  334. data/vendor/plugins/active_scaffold/test/mock_app/config/initializers/new_rails_defaults.rb +19 -0
  335. data/vendor/plugins/active_scaffold/test/mock_app/config/initializers/session_store.rb +15 -0
  336. data/vendor/plugins/active_scaffold/test/mock_app/config/locales/en.yml +5 -0
  337. data/vendor/plugins/active_scaffold/test/mock_app/config/routes.rb +43 -0
  338. data/vendor/plugins/active_scaffold/test/mock_app/db/test.sqlite3 +1 -0
  339. data/vendor/plugins/active_scaffold/test/mock_app/public/blank.html +33 -0
  340. data/vendor/plugins/active_scaffold/test/mock_app/public/images/active_scaffold/DO_NOT_EDIT +2 -0
  341. data/vendor/plugins/active_scaffold/test/mock_app/public/images/active_scaffold/default/add.gif +0 -0
  342. data/vendor/plugins/active_scaffold/test/mock_app/public/images/active_scaffold/default/arrow_down.gif +0 -0
  343. data/vendor/plugins/active_scaffold/test/mock_app/public/images/active_scaffold/default/arrow_up.gif +0 -0
  344. data/vendor/plugins/active_scaffold/test/mock_app/public/images/active_scaffold/default/close.gif +0 -0
  345. data/vendor/plugins/active_scaffold/test/mock_app/public/images/active_scaffold/default/cross.png +0 -0
  346. data/vendor/plugins/active_scaffold/test/mock_app/public/images/active_scaffold/default/indicator-small.gif +0 -0
  347. data/vendor/plugins/active_scaffold/test/mock_app/public/images/active_scaffold/default/indicator.gif +0 -0
  348. data/vendor/plugins/active_scaffold/test/mock_app/public/images/active_scaffold/default/magnifier.png +0 -0
  349. data/vendor/plugins/active_scaffold/test/mock_app/public/javascripts/active_scaffold/DO_NOT_EDIT +2 -0
  350. data/vendor/plugins/active_scaffold/test/mock_app/public/javascripts/active_scaffold/default/active_scaffold.js +532 -0
  351. data/vendor/plugins/active_scaffold/test/mock_app/public/javascripts/active_scaffold/default/dhtml_history.js +867 -0
  352. data/vendor/plugins/active_scaffold/test/mock_app/public/javascripts/active_scaffold/default/form_enhancements.js +117 -0
  353. data/vendor/plugins/active_scaffold/test/mock_app/public/javascripts/active_scaffold/default/rico_corner.js +370 -0
  354. data/vendor/plugins/active_scaffold/test/mock_app/public/stylesheets/active_scaffold/DO_NOT_EDIT +2 -0
  355. data/vendor/plugins/active_scaffold/test/mock_app/public/stylesheets/active_scaffold/default/stylesheet-ie.css +35 -0
  356. data/vendor/plugins/active_scaffold/test/mock_app/public/stylesheets/active_scaffold/default/stylesheet.css +845 -0
  357. data/vendor/plugins/active_scaffold/test/model_stub.rb +17 -1
  358. data/vendor/plugins/active_scaffold/test/test_helper.rb +31 -5
  359. data/vendor/plugins/active_scaffold/uninstall.rb +2 -1
  360. data/vendor/plugins/active_scaffold_form_observation/init.rb +1 -1
  361. data/vendor/plugins/active_scaffold_form_observation/lib/active_scaffold_form_observation.rb +1 -2
  362. data/vendor/plugins/active_scaffold_full_refresh/init.rb +2 -0
  363. data/vendor/plugins/active_scaffold_full_refresh/lib/active_scaffold_full_refresh.rb +29 -0
  364. data/vendor/plugins/acts_as_money/{tasks → lib/tasks}/money_tasks.rake +0 -0
  365. metadata +668 -563
  366. data/app/model_views/client_accounting.rb +0 -5
  367. data/app/model_views/invoices_with_total.rb +0 -41
  368. data/app/views/admin/activities/_adjustment_column.rhtml +0 -23
  369. data/app/views/admin/activities_with_prices/move_to_invoice.rjs +0 -9
  370. data/app/views/notifier/invoice_available.html.rhtml +0 -5
  371. data/public/javascripts/prototype.js-1.6.0.3 +0 -4320
  372. data/test/functional/admin/activities_controller_test.rb +0 -8
  373. data/test/functional/admin/adjustments_controller_test.rb +0 -8
  374. data/test/functional/admin/client_accounting_controller_test.rb +0 -8
  375. data/test/functional/admin/client_financial_transactions_controller_test.rb +0 -8
  376. data/test/functional/admin/client_representatives_controller_test.rb +0 -8
  377. data/test/functional/admin/clients_controller_test.rb +0 -8
  378. data/test/functional/admin/employee_client_labor_rate_controller_test.rb +0 -8
  379. data/test/functional/admin/employees/slimtimer_controller_test.rb +0 -8
  380. data/test/functional/admin/employees_controller_test.rb +0 -8
  381. data/test/functional/admin/invoices_controller_test.rb +0 -8
  382. data/test/functional/admin/labors_controller_test.rb +0 -8
  383. data/test/functional/admin/materials_controller_test.rb +0 -8
  384. data/test/functional/admin/payments_controller_test.rb +0 -8
  385. data/test/functional/admin/proposals_controller_test.rb +0 -8
  386. data/test/functional/admin/settings_controller_test.rb +0 -8
  387. data/test/functional/authentication_controller_test.rb +0 -8
  388. data/vendor/plugins/active_scaffold/frontends/default/views/_form_association_record.html.erb +0 -27
  389. data/vendor/plugins/active_scaffold/frontends/default/views/_live_search.html.erb +0 -25
  390. data/vendor/plugins/active_scaffold/frontends/default/views/form_messages_on_create.js.rjs +0 -2
  391. data/vendor/plugins/active_scaffold/frontends/default/views/form_messages_on_update.js.rjs +0 -2
  392. data/vendor/plugins/active_scaffold/lib/active_scaffold/actions/live_search.rb +0 -46
  393. data/vendor/plugins/active_scaffold/lib/active_scaffold/config/live_search.rb +0 -52
  394. data/vendor/plugins/active_scaffold/lib/active_scaffold/locale/en.rb +0 -66
  395. data/vendor/plugins/active_scaffold/lib/extensions/name_option_for_datetime.rb +0 -12
  396. data/vendor/plugins/active_scaffold/test/misc/active_record_permissions.rb +0 -154
@@ -1,6 +1,6 @@
1
1
  class CreateSettings < ActiveRecord::Migration
2
2
  def self.up
3
- create_table( :settings, :options => 'TYPE=InnoDB') do |t|
3
+ create_table( :settings) do |t|
4
4
  t.string :keyname, :label
5
5
  t.text :keyval, :description
6
6
 
@@ -12,7 +12,7 @@ class CreateCredentialsMigrateRepresentatives < ActiveRecord::Migration
12
12
  # Phase 1: Since we're here - habtm the client_representatives
13
13
  say_with_time "Migrating Client Representatives" do
14
14
  # Create the Representatives to clients habtm relationship
15
- create_table( :client_representatives_clients, :options => 'TYPE=InnoDB', :id => false ) do |t|
15
+ create_table( :client_representatives_clients, :id => false ) do |t|
16
16
  t.integer :client_id, :client_representative_id
17
17
  end
18
18
 
@@ -52,7 +52,7 @@ class CreateCredentialsMigrateRepresentatives < ActiveRecord::Migration
52
52
  say_with_time "Migrating Credentials" do
53
53
 
54
54
  # First we create the credentials table:
55
- create_table( :credentials, :options => 'TYPE=InnoDB') do |t|
55
+ create_table( :credentials) do |t|
56
56
  t.string :email_address, :password_hash
57
57
 
58
58
  t.integer :failed_login_count, :null => false, :default => 0
@@ -107,8 +107,8 @@ class MoneyToCents < ActiveRecord::Migration
107
107
 
108
108
  def self.up
109
109
  CONVERT_FIELDS.each_pair do |klass, cols|
110
- cols.to_a.each do |col|
111
- klass.update_all('%s = %s * 100' % (col.to_s.to_a * 2) )
110
+ [cols].flatten.each do |col|
111
+ klass.update_all('%s = %s * 100' % ([col.to_s] * 2) )
112
112
  change_column klass.table_name, col, :integer
113
113
  rename_column klass.table_name, col, ('%s_in_cents' % col.to_s ).to_sym
114
114
  end
@@ -72,17 +72,19 @@ ActiveRecord::Schema.define(:version => 29) do
72
72
  t.datetime "created_at"
73
73
  end
74
74
 
75
- create_table "client_finance_transactions", :force => true do |t|
75
+ create_table "client_finance_transactions", :id => false, :force => true do |t|
76
+ t.string "id", :limit => 18, :default => "", :null => false
76
77
  t.integer "client_id"
77
78
  t.datetime "date"
78
- t.binary "description", :limit => 16777215
79
+ t.text "description", :limit => 16777215
79
80
  t.integer "amount_in_cents", :limit => 34, :precision => 34, :scale => 0
80
81
  end
81
82
 
82
- create_table "client_finance_transactions_union", :force => true do |t|
83
+ create_table "client_finance_transactions_union", :id => false, :force => true do |t|
84
+ t.string "id", :limit => 18, :default => "", :null => false
83
85
  t.integer "client_id"
84
86
  t.datetime "date"
85
- t.binary "description", :limit => 16777215
87
+ t.text "description", :limit => 16777215
86
88
  t.integer "amount_in_cents", :limit => 34, :precision => 34, :scale => 0
87
89
  end
88
90
 
@@ -120,7 +122,8 @@ ActiveRecord::Schema.define(:version => 29) do
120
122
  t.boolean "is_active", :default => true, :null => false
121
123
  end
122
124
 
123
- create_table "clients_with_balances", :force => true do |t|
125
+ create_table "clients_with_balances", :id => false, :force => true do |t|
126
+ t.integer "id", :default => 0, :null => false
124
127
  t.string "company_name"
125
128
  t.string "address1"
126
129
  t.string "address2"
@@ -220,7 +223,8 @@ ActiveRecord::Schema.define(:version => 29) do
220
223
  t.integer "amount_paid_in_cents", :limit => 32, :precision => 32, :scale => 0
221
224
  end
222
225
 
223
- create_table "invoices_with_totals", :force => true do |t|
226
+ create_table "invoices_with_totals", :id => false, :force => true do |t|
227
+ t.integer "id", :default => 0, :null => false
224
228
  t.integer "client_id"
225
229
  t.text "comments"
226
230
  t.datetime "issued_on"
@@ -251,10 +255,6 @@ ActiveRecord::Schema.define(:version => 29) do
251
255
  add_index "payments", ["client_id"], :name => "index_payments_on_client_id"
252
256
  add_index "payments", ["payment_method_id"], :name => "index_payments_on_payment_method_id"
253
257
 
254
- create_table "schema_info", :id => false, :force => true do |t|
255
- t.integer "version"
256
- end
257
-
258
258
  create_table "sessions", :force => true do |t|
259
259
  t.string "session_id", :null => false
260
260
  t.text "data"
@@ -3,8 +3,8 @@ BRISKBILLS_ROOT = File.expand_path(File.join(File.dirname(__FILE__), "..")) unle
3
3
  unless defined? BriskBills::Version
4
4
  module BriskBills::Version
5
5
  Major = '0'
6
- Minor = '7'
7
- Tiny = '0'
6
+ Minor = '8'
7
+ Tiny = '1'
8
8
 
9
9
  class << self
10
10
  def to_s
@@ -1,6 +1,6 @@
1
1
  require 'rubygems'
2
- gem 'rails', '= 2.3.2'
3
- require 'initializer' # This is the rails-2.3.2/lib/initializer
2
+ gem 'rails', '= 2.3.16'
3
+ require 'initializer' # This is the rails-2.3.8/lib/initializer
4
4
 
5
5
  module BriskBills
6
6
 
@@ -44,7 +44,7 @@ BriskBills::Initializer.run do |config|
44
44
  # Make sure the secret is at least 30 characters and all random,
45
45
  # no regular words or you'll be exposed to dictionary attacks.
46
46
  config.action_controller.session = {
47
- :session_key => '_brisk-bills_session',
47
+ :key => '_brisk-bills_session',
48
48
  :secret => '<%= ([nil]*54).collect{character_pool[rand(character_pool.length)]}.join%>'
49
49
  }
50
50
 
@@ -0,0 +1,48 @@
1
+ # I ended up using this in a couple tools - so, I gave it its own library
2
+
3
+ # This got a little complicated. But, it works well ansd should be reuseable.
4
+ # I didn't put a check in here to ensure that every data row is of the same length as the header.
5
+ # But they should be - or else weirdness will ensue
6
+ def pp_table(header,data)
7
+ row_prefix = "| "
8
+ row_suffix = " |"
9
+ col_join = " | "
10
+ header_colsep = "-+-"
11
+ colsep_prefix = "+-"
12
+ colsep_suffix = "-+"
13
+ table_corner = "+"
14
+
15
+ # This is a quick way to calculate each column of the data's max string-width:
16
+ array_col_widths = ([header]+data).inject(Array.new(header.length,0)){|ret,row|
17
+ row.each_with_index{|f,i| ret[i] = f.length if f.to_s.length > ret[i] }
18
+ ret
19
+ }
20
+
21
+ col_sprintf = row_prefix+array_col_widths.collect{|w| "%-#{w}s" }.join(col_join)+row_suffix
22
+
23
+ table_width = (
24
+ array_col_widths+[
25
+ (array_col_widths.length-1)*col_join.length,
26
+ row_prefix.length,
27
+ row_suffix.length
28
+ ]
29
+ ).reduce(:+)
30
+
31
+ table_ends = [table_corner,"-" * (table_width-table_corner.length*2),table_corner].join
32
+
33
+ puts [
34
+ table_ends, # Table-top
35
+ col_sprintf % header, # Header row
36
+ [ # Header/data Separator
37
+ colsep_prefix,
38
+ array_col_widths.collect{|w| "-" * w}.join(header_colsep),
39
+ colsep_suffix
40
+ ].join,
41
+ data.collect{|a| col_sprintf % a}, # Data rows
42
+ table_ends, # Table-bottom
43
+ ].flatten.join("\n")
44
+ end
45
+
46
+ def humanize_bool(v)
47
+ v ? "Yes" : "No"
48
+ end
@@ -4,20 +4,7 @@ namespace :brisk_bills do
4
4
  task :create_last_months_invoices => :environment do
5
5
  end_of_last_month = Time.utc(*Time.now.to_a).last_month.end_of_month
6
6
 
7
- invoiceable_client_ids = Activity.find(
8
- :all,
9
- :select => 'DISTINCT client_id',
10
- :conditions => [
11
- [
12
- 'is_published = ?',
13
- 'invoice_id IS NULL',
14
- 'client_id IS NOT NULL',
15
- 'occurred_on <= ?'
16
- ].join(' AND '),
17
- true,
18
- end_of_last_month
19
- ]
20
- ).collect{|a| a.client.id}
7
+ invoiceable_client_ids = Client.find_invoiceable_clients_at(end_of_last_month)
21
8
 
22
9
  if invoiceable_client_ids.length > 0
23
10
  all_activity_types = ActivityType.find(:all)
@@ -38,7 +38,7 @@ namespace :brisk_bills do
38
38
  "CREATE DATABASE `%s`;" % envconf[:database],
39
39
  "USE `%s`;" % envconf[:database],
40
40
  "GRANT ALL PRIVILEGES ON `%s`.* TO `%s`@`%s` IDENTIFIED BY '%s';" % [envconf[:database], envconf[:username],envconf[:host],envconf[:password]]
41
- ].join ("\n ")
41
+ ].join("\n ")
42
42
  ) if db_version.nil?
43
43
 
44
44
  # Let's run migrations?
@@ -93,4 +93,4 @@ namespace :brisk_bills do
93
93
  exit
94
94
  end
95
95
  end
96
- end
96
+ end
@@ -40,7 +40,8 @@ namespace 'package' do
40
40
  s.add_dependency 'slimtimer4r', '>= 0.2.4'
41
41
  s.add_dependency 'money', '>= 2.2.0'
42
42
  s.add_dependency 'mysql', '>= 2.7'
43
- s.add_dependency 'rails', '>= 2.3.2'
43
+ s.add_dependency 'rails', '= 2.3.16'
44
+ s.add_dependency 'i18n', '= 0.4.2'
44
45
 
45
46
  s.has_rdoc = true
46
47
  s.rdoc_options = [
@@ -0,0 +1,110 @@
1
+ require 'libpptable'
2
+ namespace :brisk_bills do
3
+ desc "Check for inconsistencies in the invoice & payment assignments"
4
+
5
+ task :payment_assignment_consistency_check => :environment do
6
+
7
+ # NOTE: One way to 'fix' any problems with your invoice assignments is to:
8
+ # InvoicePayment.find(:all).each{|ip| ip.destroy}
9
+ # Payment.find(:all).each{|pmt| if pmt.amount > Money.new(0) then pmt.invoice_assignments = pmt.client.recommend_invoice_assignments_for(pmt.amount); pmt.save! end }
10
+ # But, keep in mind that this basically destroys all the associations you may have worked so hard on...
11
+
12
+ # We use this as a helper for each test below
13
+ def db_check(name, &block)
14
+ print ' - %s...' % name
15
+ records = block.call
16
+
17
+ puts (records.length > 0) ?
18
+ ("ERROR: %d records Found" % records.length) :
19
+ "PASS"
20
+ end
21
+
22
+ pp_table(
23
+ ["Table", "Record Count"],
24
+ [Invoice,Payment,InvoicePayment,Activity,Client].collect{|klass|
25
+ [klass.table_name, klass.count]
26
+ }
27
+ )
28
+
29
+ puts
30
+ puts "Tests: "
31
+
32
+ # Test : Check for InvoicePayments <= 0
33
+ db_check 'Testing for assignments with an invalid amount' do
34
+ InvoicePayment.find(:all, :conditions => ['amount_in_cents <= 0'])
35
+ end
36
+
37
+ # Test : Check for InvoicePayments which are orphaned from an invoice or payment
38
+ # (This means NULL or an id to a payment/invoice that's not in the db
39
+ db_check 'Testing for assignments with an NULL payment_id or invoice_id' do
40
+ InvoicePayment.find(
41
+ :all,
42
+ :conditions => ['invoice_id IS NULL OR payment_id IS NULL']
43
+ )
44
+ end
45
+
46
+ db_check 'Testing for assignments with a missing payment or invoice' do
47
+ InvoicePayment.find(
48
+ :all,
49
+ :select => [
50
+ 'invoice_payments.id',
51
+ 'payments.id AS payment_id',
52
+ 'invoices.id AS invoice_id'
53
+ ].join(','),
54
+ :joins => [
55
+ 'LEFT JOIN payments ON invoice_payments.payment_id = payments.id',
56
+ 'LEFT JOIN invoices ON invoice_payments.invoice_id = invoices.id'
57
+ ].join(' '),
58
+ :conditions => [
59
+ 'payments.id IS NULL OR invoices.id IS NULL'
60
+ ]
61
+ )
62
+ end
63
+
64
+ # Test : Ensure that the InvoicePayments for each invoice don't exceed its total amount
65
+ db_check "Testing for payments whose assignments exceed the payment total" do
66
+ Payment.find(
67
+ :all,
68
+ :select => [
69
+ 'payments.id',
70
+ 'payments.amount_in_cents',
71
+ 'ip.allocated_in_cents'
72
+ ].join(','),
73
+ :joins => 'LEFT JOIN (
74
+ SELECT
75
+ invoice_payments.payment_id,
76
+ SUM(invoice_payments.amount_in_cents) AS allocated_in_cents
77
+ FROM invoice_payments
78
+ GROUP BY invoice_payments.payment_id
79
+ ) AS ip ON ip.payment_id = payments.id ',
80
+ :conditions => 'ip.allocated_in_cents > payments.amount_in_cents'
81
+ )
82
+ end
83
+
84
+ # Test : Ensure that the InvoicePayments for each payment don't exceed its total amount
85
+ db_check "Testing for invoices whose assignments exceed the invoice total" do
86
+ # This one gets a little complicated since the invoice totals query is complicated
87
+ # and I don't want to make this query completely ridiculous.
88
+
89
+ # Generate a map that tracks allocations:
90
+ invoice_allocations = InvoicePayment.find(
91
+ :all,
92
+ :select => [
93
+ 'id',
94
+ 'invoice_id',
95
+ 'SUM(amount_in_cents) AS allocated_in_cents'
96
+ ].join(' ,'),
97
+ :group => 'invoice_id'
98
+ ).inject({}){ |ret,ip| ret.merge({ip.invoice_id => ip.allocated_in_cents.to_i}) }
99
+
100
+
101
+ Invoice.find_with_totals( :all, :conditions => ['is_published = ?', true] ).find_all{|inv|
102
+ (
103
+ invoice_allocations.has_key? inv.id and
104
+ invoice_allocations[inv.id] > inv.amount_in_cents.to_i
105
+ )
106
+ }
107
+ end
108
+
109
+ end
110
+ end
@@ -7,12 +7,12 @@ class String
7
7
 
8
8
  options.each_char do |c|
9
9
  mods |= case c
10
- when 'i': Regexp::IGNORECASE
11
- when 'x': Regexp::EXTENDED
12
- when 'm': Regexp::MULTILINE
10
+ when 'i' then Regexp::IGNORECASE
11
+ when 'x' then Regexp::EXTENDED
12
+ when 'm' then Regexp::MULTILINE
13
13
  end
14
14
  end unless options.nil? or options.empty?
15
15
 
16
16
  Regexp.new source, mods
17
17
  end
18
- end
18
+ end
@@ -1,430 +1,532 @@
1
- if (typeof Prototype == 'undefined')
2
- {
3
- warning = "ActiveScaffold Error: Prototype could not be found. Please make sure that your application's layout includes prototype.js (e.g. <%= javascript_include_tag :defaults %>) *before* it includes active_scaffold.js (e.g. <%= active_scaffold_includes %>).";
4
- alert(warning);
5
- }
6
- if (Prototype.Version.substring(0, 3) != '1.6')
7
- {
8
- warning = "ActiveScaffold Error: Prototype version 1.6.x is required. Please update prototype.js (rake rails:update:javascripts).";
9
- alert(warning);
10
- }
11
-
12
- /*
13
- * Simple utility methods
14
- */
15
-
16
- var ActiveScaffold = {
17
- records_for: function(tbody_id) {
18
- var rows = [];
19
- var child = $(tbody_id).down('.record');
20
- while (child) {
21
- rows.push(child);
22
- child = child.next('.record');
23
- }
24
- return rows;
25
- },
26
- stripe: function(tbody_id) {
27
- var even = false;
28
- var rows = this.records_for(tbody_id);
29
- for (var i = 0; i < rows.length; i++) {
30
- var child = rows[i];
31
- //Make sure to skip rows that are create or edit rows or messages
32
- if (child.tagName != 'SCRIPT'
33
- && !child.hasClassName("create")
34
- && !child.hasClassName("update")
35
- && !child.hasClassName("inline-adapter")
36
- && !child.hasClassName("active-scaffold-calculations")) {
37
-
38
- if (even) child.addClassName("even-record");
39
- else child.removeClassName("even-record");
40
-
41
- even = !even;
42
- }
43
- }
44
- },
45
- hide_empty_message: function(tbody, empty_message_id) {
46
- if (this.records_for(tbody).length != 0) {
47
- $(empty_message_id).hide();
48
- }
49
- },
50
- reload_if_empty: function(tbody, url) {
51
- var content_container_id = tbody.replace('tbody', 'content');
52
- if (this.records_for(tbody).length == 0) {
53
- new Ajax.Updater($(content_container_id), url, {
54
- method: 'get',
55
- asynchronous: true,
56
- evalScripts: true
57
- });
58
- }
59
- },
60
- removeSortClasses: function(scaffold_id) {
61
- $$('#' + scaffold_id + ' td.sorted').each(function(element) {
62
- element.removeClassName("sorted");
63
- });
64
- $$('#' + scaffold_id + ' th.sorted').each(function(element) {
65
- element.removeClassName("sorted");
66
- element.removeClassName("asc");
67
- element.removeClassName("desc");
68
- });
69
- },
70
- decrement_record_count: function(scaffold_id) {
71
- // decrement the last record count, firsts record count are in nested lists
72
- count = $$('#' + scaffold_id + ' span.active-scaffold-records').last();
73
- count.innerHTML = parseInt(count.innerHTML) - 1;
74
- },
75
- increment_record_count: function(scaffold_id) {
76
- // increment the last record count, firsts record count are in nested lists
77
- count = $$('#' + scaffold_id + ' span.active-scaffold-records').last();
78
- count.innerHTML = parseInt(count.innerHTML) + 1;
79
- },
80
-
81
- server_error_response: '',
82
- report_500_response: function(active_scaffold_id) {
83
- messages_container = $(active_scaffold_id).down('td.messages-container');
84
- new Insertion.Top(messages_container, this.server_error_response);
85
- }
86
- }
87
-
88
- /*
89
- * DHTML history tie-in
90
- */
91
- function addActiveScaffoldPageToHistory(url, active_scaffold_id) {
92
- if (typeof dhtmlHistory == 'undefined') return; // it may not be loaded
93
-
94
- var array = url.split('?');
95
- var qs = new Querystring(array[1]);
96
- var sort = qs.get('sort')
97
- var dir = qs.get('sort_direction')
98
- var page = qs.get('page')
99
- if (sort || dir || page) dhtmlHistory.add(active_scaffold_id+":"+page+":"+sort+":"+dir, url);
100
- }
101
-
102
- /*
103
- * Add-ons/Patches to Prototype
104
- */
105
-
106
- /* patch to support replacing TR/TD/TBODY in Internet Explorer, courtesy of http://dev.rubyonrails.org/ticket/4273 */
107
- Element.replace = function(element, html) {
108
- element = $(element);
109
- if (element.outerHTML) {
110
- try {
111
- element.outerHTML = html.stripScripts();
112
- } catch (e) {
113
- var tn = element.tagName;
114
- if(tn=='TBODY' || tn=='TR' || tn=='TD')
115
- {
116
- var tempDiv = document.createElement("div");
117
- tempDiv.innerHTML = '<table id="tempTable" style="display: none">' + html.stripScripts() + '</table>';
118
- element.parentNode.replaceChild(tempDiv.getElementsByTagName(tn).item(0), element);
119
- }
120
- else throw e;
121
- }
122
- } else {
123
- var range = element.ownerDocument.createRange();
124
- /* patch to fix <form> replaces in Firefox. see http://dev.rubyonrails.org/ticket/8010 */
125
- range.selectNodeContents(element.parentNode);
126
- element.parentNode.replaceChild(range.createContextualFragment(html.stripScripts()), element);
127
- }
128
- setTimeout(function() {html.evalScripts()}, 10);
129
- return element;
130
- };
131
-
132
- /*
133
- * URL modification support. Incomplete functionality.
134
- */
135
- Object.extend(String.prototype, {
136
- append_params: function(params) {
137
- url = this;
138
- if (url.indexOf('?') == -1) url += '?';
139
- else if (url.lastIndexOf('&') != url.length) url += '&';
140
-
141
- url += $H(params).collect(function(item) {
142
- return item.key + '=' + item.value;
143
- }).join('&');
144
-
145
- return url;
146
- }
147
- });
148
-
149
- /*
150
- * Prototype's implementation was throwing an error instead of false
151
- */
152
- Element.Methods.Simulated = {
153
- hasAttribute: function(element, attribute) {
154
- var t = Element._attributeTranslations;
155
- attribute = (t.names && t.names[attribute]) || attribute;
156
- // Return false if we get an error here
157
- try {
158
- return $(element).getAttributeNode(attribute).specified;
159
- } catch (e) {
160
- return false;
161
- }
162
- }
163
- };
164
-
165
- /**
166
- * A set of links. As a set, they can be controlled such that only one is "open" at a time, etc.
167
- */
168
- ActiveScaffold.Actions = new Object();
169
- ActiveScaffold.Actions.Abstract = function(){}
170
- ActiveScaffold.Actions.Abstract.prototype = {
171
- initialize: function(links, target, loading_indicator, options) {
172
- this.target = $(target);
173
- this.loading_indicator = $(loading_indicator);
174
- this.options = options;
175
- this.links = links.collect(function(link) {
176
- return this.instantiate_link(link);
177
- }.bind(this));
178
- },
179
-
180
- instantiate_link: function(link) {
181
- throw 'unimplemented'
182
- }
183
- }
184
-
185
- /**
186
- * A DataStructures::ActionLink, represented in JavaScript.
187
- * Concerned with AJAX-enabling a link and adapting the result for insertion into the table.
188
- */
189
- ActiveScaffold.ActionLink = new Object();
190
- ActiveScaffold.ActionLink.Abstract = function(){}
191
- ActiveScaffold.ActionLink.Abstract.prototype = {
192
- initialize: function(a, target, loading_indicator) {
193
- this.tag = $(a);
194
- this.url = this.tag.href;
195
- this.method = 'get';
196
- if(this.url.match('_method=delete')){
197
- this.method = 'delete';
198
- } else if(this.url.match('_method=post')){
199
- this.method = 'post';
200
- }
201
- this.target = target;
202
- this.loading_indicator = loading_indicator;
203
- this.hide_target = false;
204
- this.position = this.tag.getAttribute('position');
205
- this.page_link = this.tag.getAttribute('page_link');
206
-
207
- this.onclick = this.tag.onclick;
208
- this.tag.onclick = null;
209
- this.tag.observe('click', function(event) {
210
- this.open();
211
- Event.stop(event);
212
- }.bind(this));
213
-
214
- this.tag.action_link = this;
215
- },
216
-
217
- open: function() {
218
- if (this.is_disabled()) return;
219
-
220
- if (this.tag.hasAttribute( "dhtml_confirm")) {
221
- if (this.onclick) this.onclick();
222
- return;
223
- } else {
224
- if (this.onclick && !this.onclick()) return;//e.g. confirmation messages
225
- this.open_action();
226
- }
227
- },
228
-
229
- open_action: function() {
230
- if (this.position) this.disable();
231
-
232
- if (this.page_link) {
233
- window.location = this.url;
234
- } else {
235
- if (this.loading_indicator) this.loading_indicator.style.visibility = 'visible';
236
- new Ajax.Request(this.url, {
237
- asynchronous: true,
238
- evalScripts: true,
239
- method: this.method,
240
- onSuccess: function(request) {
241
- if (this.position) {
242
- this.insert(request.responseText);
243
- if (this.hide_target) this.target.hide();
244
- } else {
245
- request.evalResponse();
246
- }
247
- }.bind(this),
248
-
249
- onFailure: function(request) {
250
- ActiveScaffold.report_500_response(this.scaffold_id());
251
- if (this.position) this.enable()
252
- }.bind(this),
253
-
254
- onComplete: function(request) {
255
- if (this.loading_indicator) this.loading_indicator.style.visibility = 'hidden';
256
- }.bind(this)
257
- });
258
- }
259
- },
260
-
261
- insert: function(content) {
262
- throw 'unimplemented'
263
- },
264
-
265
- close: function() {
266
- this.enable();
267
- this.adapter.remove();
268
- if (this.hide_target) this.target.show();
269
- },
270
-
271
- register_cancel_hooks: function() {
272
- // anything in the insert with a class of cancel gets the closer method, and a reference to this object for good measure
273
- var self = this;
274
- this.adapter.select('.cancel').each(function(elem) {
275
- elem.observe('click', this.close_handler.bind(this));
276
- elem.link = self;
277
- }.bind(this))
278
- },
279
-
280
- reload: function() {
281
- this.close();
282
- this.open();
283
- },
284
-
285
- get_new_adapter_id: function() {
286
- var id = 'adapter_';
287
- var i = 0;
288
- while ($(id + i)) i++;
289
- return id + i;
290
- },
291
-
292
- enable: function() {
293
- return this.tag.removeClassName('disabled');
294
- },
295
-
296
- disable: function() {
297
- return this.tag.addClassName('disabled');
298
- },
299
-
300
- is_disabled: function() {
301
- return this.tag.hasClassName('disabled');
302
- },
303
-
304
- scaffold_id: function() {
305
- return this.tag.up('div.active-scaffold').id;
306
- }
307
- }
308
-
309
- /**
310
- * Concrete classes for record actions
311
- */
312
- ActiveScaffold.Actions.Record = Class.create();
313
- ActiveScaffold.Actions.Record.prototype = Object.extend(new ActiveScaffold.Actions.Abstract(), {
314
- instantiate_link: function(link) {
315
- var l = new ActiveScaffold.ActionLink.Record(link, this.target, this.loading_indicator);
316
- l.refresh_url = this.options.refresh_url;
317
- if (l.position) l.url = l.url.append_params({adapter: '_list_inline_adapter'});
318
- l.set = this;
319
- return l;
320
- }
321
- });
322
-
323
- ActiveScaffold.ActionLink.Record = Class.create();
324
- ActiveScaffold.ActionLink.Record.prototype = Object.extend(new ActiveScaffold.ActionLink.Abstract(), {
325
- close_previous_adapter: function() {
326
- this.set.links.each(function(item) {
327
- if (item.url != this.url && item.is_disabled() && item.adapter) item.close();
328
- }.bind(this));
329
- },
330
-
331
- insert: function(content) {
332
- this.close_previous_adapter();
333
-
334
- if (this.position == 'replace') {
335
- this.position = 'after';
336
- this.hide_target = true;
337
- }
338
-
339
- if (this.position == 'after') {
340
- new Insertion.After(this.target, content);
341
- this.adapter = this.target.next();
342
- }
343
- else if (this.position == 'before') {
344
- new Insertion.Before(this.target, content);
345
- this.adapter = this.target.previous();
346
- }
347
- else {
348
- return false;
349
- }
350
-
351
- this.adapter.down('a.inline-adapter-close').observe('click', this.close_handler.bind(this));
352
- this.register_cancel_hooks();
353
-
354
- new Effect.Highlight(this.adapter.down('td'));
355
- },
356
-
357
- close_handler: function(event) {
358
- this.close_with_refresh();
359
- if (event) Event.stop(event);
360
- },
361
-
362
- /* it might simplify things to just override the close function. then the Record and Table links could share more code ... wouldn't need custom close_handler functions, for instance */
363
- close_with_refresh: function() {
364
- new Ajax.Request(this.refresh_url, {
365
- asynchronous: true,
366
- evalScripts: true,
367
- method: this.method,
368
- onSuccess: function(request) {
369
- Element.replace(this.target, request.responseText);
370
- var new_target = $(this.target.id);
371
- if (this.target.hasClassName('even-record')) new_target.addClassName('even-record');
372
- this.target = new_target;
373
- this.close();
374
- }.bind(this),
375
-
376
- onFailure: function(request) {
377
- ActiveScaffold.report_500_response(this.scaffold_id());
378
- }
379
- });
380
- },
381
-
382
- enable: function() {
383
- this.set.links.each(function(item) {
384
- if (item.url != this.url) return;
385
- item.tag.removeClassName('disabled');
386
- }.bind(this));
387
- },
388
-
389
- disable: function() {
390
- this.set.links.each(function(item) {
391
- if (item.url != this.url) return;
392
- item.tag.addClassName('disabled');
393
- }.bind(this));
394
- }
395
- });
396
-
397
- /**
398
- * Concrete classes for table actions
399
- */
400
- ActiveScaffold.Actions.Table = Class.create();
401
- ActiveScaffold.Actions.Table.prototype = Object.extend(new ActiveScaffold.Actions.Abstract(), {
402
- instantiate_link: function(link) {
403
- var l = new ActiveScaffold.ActionLink.Table(link, this.target, this.loading_indicator);
404
- if (l.position) l.url = l.url.append_params({adapter: '_list_inline_adapter'});
405
- return l;
406
- }
407
- });
408
-
409
- ActiveScaffold.ActionLink.Table = Class.create();
410
- ActiveScaffold.ActionLink.Table.prototype = Object.extend(new ActiveScaffold.ActionLink.Abstract(), {
411
- insert: function(content) {
412
- if (this.position == 'top') {
413
- new Insertion.Top(this.target, content);
414
- this.adapter = this.target.immediateDescendants().first();
415
- }
416
- else {
417
- throw 'Unknown position "' + this.position + '"'
418
- }
419
-
420
- this.adapter.down('a.inline-adapter-close').observe('click', this.close_handler.bind(this));
421
- this.register_cancel_hooks();
422
-
423
- new Effect.Highlight(this.adapter.down('td'));
424
- },
425
-
426
- close_handler: function(event) {
427
- this.close();
428
- if (event) Event.stop(event);
429
- }
430
- });
1
+ if (typeof Prototype == 'undefined')
2
+ {
3
+ warning = "ActiveScaffold Error: Prototype could not be found. Please make sure that your application's layout includes prototype.js (e.g. <%= javascript_include_tag :defaults %>) *before* it includes active_scaffold.js (e.g. <%= active_scaffold_includes %>).";
4
+ alert(warning);
5
+ }
6
+ if (Prototype.Version.substring(0, 3) < '1.6')
7
+ {
8
+ warning = "ActiveScaffold Error: Prototype version 1.6.x or higher is required. Please update prototype.js (rake rails:update:javascripts).";
9
+ alert(warning);
10
+ }
11
+ if (!Element.Methods.highlight) Element.addMethods({highlight: Prototype.emptyFunction});
12
+
13
+
14
+ /*
15
+ * Simple utility methods
16
+ */
17
+
18
+ var ActiveScaffold = {
19
+ records_for: function(tbody_id) {
20
+ var rows = [];
21
+ var child = $(tbody_id).down('.record');
22
+ while (child) {
23
+ rows.push(child);
24
+ child = child.next('.record');
25
+ }
26
+ return rows;
27
+ },
28
+ stripe: function(tbody_id) {
29
+ var even = false;
30
+ var rows = this.records_for(tbody_id);
31
+ for (var i = 0; i < rows.length; i++) {
32
+ var child = rows[i];
33
+ //Make sure to skip rows that are create or edit rows or messages
34
+ if (child.tagName != 'SCRIPT'
35
+ && !child.hasClassName("create")
36
+ && !child.hasClassName("update")
37
+ && !child.hasClassName("inline-adapter")
38
+ && !child.hasClassName("active-scaffold-calculations")) {
39
+
40
+ if (even) child.addClassName("even-record");
41
+ else child.removeClassName("even-record");
42
+
43
+ even = !even;
44
+ }
45
+ }
46
+ },
47
+ hide_empty_message: function(tbody, empty_message_id) {
48
+ if (this.records_for(tbody).length != 0) {
49
+ $(empty_message_id).hide();
50
+ }
51
+ },
52
+ reload_if_empty: function(tbody, url) {
53
+ if (this.records_for(tbody).length == 0) {
54
+ new Ajax.Request(url, {
55
+ method: 'get',
56
+ asynchronous: true,
57
+ evalScripts: true
58
+ });
59
+ }
60
+ },
61
+ removeSortClasses: function(scaffold_id) {
62
+ $$('#' + scaffold_id + ' td.sorted').each(function(element) {
63
+ element.removeClassName("sorted");
64
+ });
65
+ $$('#' + scaffold_id + ' th.sorted').each(function(element) {
66
+ element.removeClassName("sorted");
67
+ element.removeClassName("asc");
68
+ element.removeClassName("desc");
69
+ });
70
+ },
71
+ decrement_record_count: function(scaffold_id) {
72
+ // decrement the last record count, firsts record count are in nested lists
73
+ count = $$('#' + scaffold_id + ' span.active-scaffold-records').last();
74
+ if (count) count.update(parseInt(count.innerHTML, 10) - 1);
75
+ },
76
+ increment_record_count: function(scaffold_id) {
77
+ // increment the last record count, firsts record count are in nested lists
78
+ count = $$('#' + scaffold_id + ' span.active-scaffold-records').last();
79
+ if (count) count.update(parseInt(count.innerHTML, 10) + 1);
80
+ },
81
+ update_row: function(row, html) {
82
+ row = $(row);
83
+ Element.replace(row, html);
84
+ var new_row = $(row.id);
85
+ if (row.hasClassName('even-record')) new_row.addClassName('even-record');
86
+ new_row.highlight();
87
+ },
88
+
89
+ server_error_response: '',
90
+ report_500_response: function(active_scaffold_id) {
91
+ messages_container = $(active_scaffold_id).down('td.messages-container');
92
+ new Insertion.Top(messages_container, this.server_error_response);
93
+ }
94
+ }
95
+
96
+ /*
97
+ * DHTML history tie-in
98
+ */
99
+ function addActiveScaffoldPageToHistory(url, active_scaffold_id) {
100
+ if (typeof dhtmlHistory == 'undefined') return; // it may not be loaded
101
+
102
+ var array = url.split('?');
103
+ var qs = new Querystring(array[1]);
104
+ var sort = qs.get('sort')
105
+ var dir = qs.get('sort_direction')
106
+ var page = qs.get('page')
107
+ if (sort || dir || page) dhtmlHistory.add(active_scaffold_id+":"+page+":"+sort+":"+dir, url);
108
+ }
109
+
110
+ /*
111
+ * Add-ons/Patches to Prototype
112
+ */
113
+
114
+ /* patch to support replacing TR/TD/TBODY in Internet Explorer, courtesy of http://dev.rubyonrails.org/ticket/4273 */
115
+ Element.replace = function(element, html) {
116
+ element = $(element);
117
+ if (element.outerHTML) {
118
+ try {
119
+ element.outerHTML = html.stripScripts();
120
+ } catch (e) {
121
+ var tn = element.tagName;
122
+ if(tn=='TBODY' || tn=='TR' || tn=='TD')
123
+ {
124
+ var tempDiv = document.createElement("div");
125
+ tempDiv.innerHTML = '<table id="tempTable" style="display: none">' + html.stripScripts() + '</table>';
126
+ element.parentNode.replaceChild(tempDiv.getElementsByTagName(tn).item(0), element);
127
+ }
128
+ else throw e;
129
+ }
130
+ } else {
131
+ var range = element.ownerDocument.createRange();
132
+ /* patch to fix <form> replaces in Firefox. see http://dev.rubyonrails.org/ticket/8010 */
133
+ range.selectNodeContents(element.parentNode);
134
+ element.parentNode.replaceChild(range.createContextualFragment(html.stripScripts()), element);
135
+ }
136
+ setTimeout(function() {html.evalScripts()}, 10);
137
+ return element;
138
+ };
139
+
140
+ /*
141
+ * URL modification support. Incomplete functionality.
142
+ */
143
+ Object.extend(String.prototype, {
144
+ append_params: function(params) {
145
+ url = this;
146
+ if (url.indexOf('?') == -1) url += '?';
147
+ else if (url.lastIndexOf('&') != url.length) url += '&';
148
+
149
+ url += $H(params).collect(function(item) {
150
+ return item.key + '=' + item.value;
151
+ }).join('&');
152
+
153
+ return url;
154
+ }
155
+ });
156
+
157
+ /*
158
+ * Prototype's implementation was throwing an error instead of false
159
+ */
160
+ Element.Methods.Simulated = {
161
+ hasAttribute: function(element, attribute) {
162
+ var t = Element._attributeTranslations;
163
+ attribute = (t.names && t.names[attribute]) || attribute;
164
+ // Return false if we get an error here
165
+ try {
166
+ return $(element).getAttributeNode(attribute).specified;
167
+ } catch (e) {
168
+ return false;
169
+ }
170
+ }
171
+ };
172
+
173
+ /**
174
+ * A set of links. As a set, they can be controlled such that only one is "open" at a time, etc.
175
+ */
176
+ ActiveScaffold.Actions = new Object();
177
+ ActiveScaffold.Actions.Abstract = Class.create({
178
+ initialize: function(links, target, loading_indicator, options) {
179
+ this.target = $(target);
180
+ this.loading_indicator = $(loading_indicator);
181
+ this.options = options;
182
+ this.links = links.collect(function(link) {
183
+ return this.instantiate_link(link);
184
+ }.bind(this));
185
+ },
186
+
187
+ instantiate_link: function(link) {
188
+ throw 'unimplemented'
189
+ }
190
+ });
191
+
192
+ /**
193
+ * A DataStructures::ActionLink, represented in JavaScript.
194
+ * Concerned with AJAX-enabling a link and adapting the result for insertion into the table.
195
+ */
196
+ ActiveScaffold.ActionLink = new Object();
197
+ ActiveScaffold.ActionLink.Abstract = Class.create({
198
+ initialize: function(a, target, loading_indicator) {
199
+ this.tag = $(a);
200
+ this.url = this.tag.href;
201
+ this.method = 'get';
202
+ if(this.url.match('_method=delete')){
203
+ this.method = 'delete';
204
+ } else if(this.url.match('_method=post')){
205
+ this.method = 'post';
206
+ } else if(this.url.match('_method=put')){
207
+ this.method = 'put';
208
+ }
209
+ this.target = target;
210
+ this.loading_indicator = loading_indicator;
211
+ this.hide_target = false;
212
+ this.position = this.tag.getAttribute('position');
213
+ this.page_link = this.tag.getAttribute('page_link');
214
+
215
+ this.onclick = this.tag.onclick;
216
+ this.tag.onclick = null;
217
+ this.tag.observe('click', function(event) {
218
+ this.open();
219
+ Event.stop(event);
220
+ }.bind(this));
221
+
222
+ this.tag.action_link = this;
223
+ },
224
+
225
+ open: function() {
226
+ if (this.is_disabled()) return;
227
+
228
+ if (this.tag.hasAttribute( "dhtml_confirm")) {
229
+ if (this.onclick) this.onclick();
230
+ return;
231
+ } else {
232
+ if (this.onclick && !this.onclick()) return;//e.g. confirmation messages
233
+ this.open_action();
234
+ }
235
+ },
236
+
237
+ open_action: function() {
238
+ if (this.position) this.disable();
239
+
240
+ if (this.page_link) {
241
+ window.location = this.url;
242
+ } else {
243
+ if (this.loading_indicator) this.loading_indicator.style.visibility = 'visible';
244
+ new Ajax.Request(this.url, {
245
+ asynchronous: true,
246
+ evalScripts: true,
247
+ method: this.method,
248
+ onSuccess: function(request) {
249
+ if (this.position) {
250
+ this.insert(request.responseText);
251
+ if (this.hide_target) this.target.hide();
252
+ } else {
253
+ request.evalResponse();
254
+ }
255
+ }.bind(this),
256
+
257
+ onFailure: function(request) {
258
+ ActiveScaffold.report_500_response(this.scaffold_id());
259
+ if (this.position) this.enable()
260
+ }.bind(this),
261
+
262
+ onComplete: function(request) {
263
+ if (this.loading_indicator) this.loading_indicator.style.visibility = 'hidden';
264
+ }.bind(this)
265
+ });
266
+ }
267
+ },
268
+
269
+ insert: function(content) {
270
+ throw 'unimplemented'
271
+ },
272
+
273
+ close: function() {
274
+ this.enable();
275
+ this.adapter.remove();
276
+ if (this.hide_target) this.target.show();
277
+ },
278
+
279
+ close_handler: function(event) {
280
+ this.close();
281
+ if (event) Event.stop(event);
282
+ },
283
+
284
+ register_cancel_hooks: function() {
285
+ // anything in the insert with a class of cancel gets the closer method, and a reference to this object for good measure
286
+ var self = this;
287
+ this.adapter.select('.cancel').each(function(elem) {
288
+ elem.observe('click', this.close_handler.bind(this));
289
+ elem.link = self;
290
+ }.bind(this))
291
+ },
292
+
293
+ reload: function() {
294
+ this.close();
295
+ this.open();
296
+ },
297
+
298
+ get_new_adapter_id: function() {
299
+ var id = 'adapter_';
300
+ var i = 0;
301
+ while ($(id + i)) i++;
302
+ return id + i;
303
+ },
304
+
305
+ enable: function() {
306
+ return this.tag.removeClassName('disabled');
307
+ },
308
+
309
+ disable: function() {
310
+ return this.tag.addClassName('disabled');
311
+ },
312
+
313
+ is_disabled: function() {
314
+ return this.tag.hasClassName('disabled');
315
+ },
316
+
317
+ scaffold_id: function() {
318
+ return this.tag.up('div.active-scaffold').id;
319
+ }
320
+ });
321
+
322
+ /**
323
+ * Concrete classes for record actions
324
+ */
325
+ ActiveScaffold.Actions.Record = Class.create(ActiveScaffold.Actions.Abstract, {
326
+ instantiate_link: function(link) {
327
+ var l = new ActiveScaffold.ActionLink.Record(link, this.target, this.loading_indicator);
328
+ l.refresh_url = this.options.refresh_url;
329
+ if (link.hasClassName('delete')) {
330
+ l.url = l.url.replace(/\/delete(\?.*)?$/, '$1');
331
+ l.url = l.url.replace(/\/delete\/(.*)/, '/destroy/$1');
332
+ }
333
+ if (l.position) l.url = l.url.append_params({adapter: '_list_inline_adapter'});
334
+ l.set = this;
335
+ return l;
336
+ }
337
+ });
338
+
339
+ ActiveScaffold.ActionLink.Record = Class.create(ActiveScaffold.ActionLink.Abstract, {
340
+ close_previous_adapter: function() {
341
+ this.set.links.each(function(item) {
342
+ if (item.url != this.url && item.is_disabled() && item.adapter) {
343
+ item.enable();
344
+ item.adapter.remove();
345
+ }
346
+ }.bind(this));
347
+ },
348
+
349
+ insert: function(content) {
350
+ this.close_previous_adapter();
351
+
352
+ if (this.position == 'replace') {
353
+ this.position = 'after';
354
+ this.hide_target = true;
355
+ }
356
+
357
+ if (this.position == 'after') {
358
+ new Insertion.After(this.target, content);
359
+ this.adapter = this.target.next();
360
+ }
361
+ else if (this.position == 'before') {
362
+ new Insertion.Before(this.target, content);
363
+ this.adapter = this.target.previous();
364
+ }
365
+ else {
366
+ return false;
367
+ }
368
+
369
+ this.adapter.down('a.inline-adapter-close').observe('click', this.close_handler.bind(this));
370
+ this.register_cancel_hooks();
371
+
372
+ this.adapter.down('td').down().highlight();
373
+ },
374
+
375
+ close: function($super, updatedRow) {
376
+ if (updatedRow) {
377
+ ActiveScaffold.update_row(this.target, updatedRow);
378
+ $super();
379
+ } else {
380
+ new Ajax.Request(this.refresh_url, {
381
+ asynchronous: true,
382
+ evalScripts: true,
383
+ method: this.method,
384
+ onSuccess: function(request) {
385
+ ActiveScaffold.update_row(this.target, request.responseText);
386
+ $super();
387
+ }.bind(this),
388
+
389
+ onFailure: function(request) {
390
+ ActiveScaffold.report_500_response(this.scaffold_id());
391
+ }
392
+ });
393
+ }
394
+ },
395
+
396
+ enable: function() {
397
+ this.set.links.each(function(item) {
398
+ if (item.url != this.url) return;
399
+ item.tag.removeClassName('disabled');
400
+ }.bind(this));
401
+ },
402
+
403
+ disable: function() {
404
+ this.set.links.each(function(item) {
405
+ if (item.url != this.url) return;
406
+ item.tag.addClassName('disabled');
407
+ }.bind(this));
408
+ }
409
+ });
410
+
411
+ /**
412
+ * Concrete classes for table actions
413
+ */
414
+ ActiveScaffold.Actions.Table = Class.create(ActiveScaffold.Actions.Abstract, {
415
+ instantiate_link: function(link) {
416
+ var l = new ActiveScaffold.ActionLink.Table(link, this.target, this.loading_indicator);
417
+ if (l.position) l.url = l.url.append_params({adapter: '_list_inline_adapter'});
418
+ return l;
419
+ }
420
+ });
421
+
422
+ ActiveScaffold.ActionLink.Table = Class.create(ActiveScaffold.ActionLink.Abstract, {
423
+ insert: function(content) {
424
+ if (this.position == 'top') {
425
+ new Insertion.Top(this.target, content);
426
+ this.adapter = this.target.immediateDescendants().first();
427
+ }
428
+ else {
429
+ throw 'Unknown position "' + this.position + '"'
430
+ }
431
+
432
+ this.adapter.down('a.inline-adapter-close').observe('click', this.close_handler.bind(this));
433
+ this.register_cancel_hooks();
434
+
435
+ this.adapter.down('td').down().highlight();
436
+ }
437
+ });
438
+
439
+ if (Ajax.InPlaceEditor) {
440
+ ActiveScaffold.InPlaceEditor = Class.create(Ajax.InPlaceEditor, {
441
+ setFieldFromAjax: function(url, options) {
442
+ var ipe = this;
443
+ $(ipe._controls.editor).remove();
444
+ new Ajax.Request(url, {
445
+ method: 'get',
446
+ onComplete: function(response) {
447
+ ipe._form.insert({top: response.responseText});
448
+ if (options.plural) {
449
+ ipe._form.getElements().each(function(el) {
450
+ if (el.type != "submit" && el.type != "image") {
451
+ el.name = ipe.options.paramName + '[]';
452
+ el.className = 'editor_field';
453
+ }
454
+ });
455
+ } else {
456
+ var fld = ipe._form.findFirstElement();
457
+ fld.name = ipe.options.paramName;
458
+ fld.className = 'editor_field';
459
+ if (ipe.options.submitOnBlur)
460
+ fld.onblur = ipe._boundSubmitHandler;
461
+ ipe._controls.editor = fld;
462
+ }
463
+ }
464
+ });
465
+ },
466
+
467
+ clonePatternField: function() {
468
+ var patternNodes = this.getPatternNodes(this.options.inplacePatternSelector);
469
+ if (patternNodes.editNode == null) {
470
+ alert('did not find any matching node for ' + this.options.editFieldSelector);
471
+ return;
472
+ }
473
+
474
+ var fld = patternNodes.editNode.cloneNode(true);
475
+ if (fld.id.length > 0) fld.id += this.options.nodeIdSuffix;
476
+ fld.name = this.options.paramName;
477
+ fld.className = 'editor_field';
478
+ this.setValue(fld, this._controls.editor.value);
479
+ if (this.options.submitOnBlur)
480
+ fld.onblur = this._boundSubmitHandler;
481
+ $(this._controls.editor).remove();
482
+ this._controls.editor = fld;
483
+ this._form.appendChild(this._controls.editor);
484
+
485
+ $A(patternNodes.additionalNodes).each(function(node) {
486
+ var patternNode = node.cloneNode(true);
487
+ if (patternNode.id.length > 0) {
488
+ patternNode.id = patternNode.id + this.options.nodeIdSuffix;
489
+ }
490
+ this._form.appendChild(patternNode);
491
+ }.bind(this));
492
+ },
493
+
494
+ getPatternNodes: function(inplacePatternSelector) {
495
+ var nodes = {editNode: null, additionalNodes: []};
496
+ var selectedNodes = $$(inplacePatternSelector);
497
+ var firstNode = selectedNodes.first();
498
+
499
+ if (typeof(firstNode) !== 'undefined') {
500
+ // AS inplace_edit_control_container -> we have to select all child nodes
501
+ // Workaround for ie which does not support css > selector
502
+ if (firstNode.className.indexOf('as_inplace_pattern') !== -1) {
503
+ selectedNodes = firstNode.childElements();
504
+ }
505
+ nodes.editNode = selectedNodes.first();
506
+ selectedNodes.shift();
507
+ nodes.additionalNodes = selectedNodes;
508
+ }
509
+ return nodes;
510
+ },
511
+
512
+ setValue: function(editField, textValue) {
513
+ var function_name = 'setValueFor' + editField.nodeName.toLowerCase();
514
+ if (typeof(this[function_name]) == 'function') {
515
+ this[function_name](editField, textValue);
516
+ } else {
517
+ editField.value = textValue;
518
+ }
519
+ },
520
+
521
+ setValueForselect: function(editField, textValue) {
522
+ var len = editField.options.length;
523
+ var i = 0;
524
+ while (i < len && editField.options[i].text != textValue) {
525
+ i++;
526
+ }
527
+ if (i < len) {
528
+ editField.value = editField.options[i].value
529
+ }
530
+ }
531
+ });
532
+ }